Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions src/php/UnifiedSnippets/Adapters/DB_Scanner_Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,36 @@
*/
abstract class DB_Scanner_Adapter extends Scanner_Base {

/**
* The wrapped importer that reads rows from the source plugin's storage.
*
* @var Plugin_Importer
*/
protected Plugin_Importer $importer;

/**
* Class constructor.
*
* @param Plugin_Importer|null $importer Optional importer override, primarily for testing.
* When null, the concrete subclass creates one via
* {@see self::create_importer()}.
*/
public function __construct( ?Plugin_Importer $importer = null ) {
$this->importer = $importer ?? $this->create_importer();
}

/**
* Construct the {@see Plugin_Importer} this adapter wraps.
*
* @return Plugin_Importer
*/
abstract protected function create_importer(): Plugin_Importer;

/**
* The source plugin's storage table name (without the WordPress table prefix).
*
* @return string
*/
abstract protected function get_table_name(): string;

/**
Expand All @@ -34,10 +56,19 @@ abstract protected function get_table_name(): string;
*/
abstract protected function map_row( array $row ): ?array;

/**
* {@inheritDoc}
*
* Delegates to the wrapped importer's `is_active()` so the adapter is available exactly
* when the source plugin would be importable.
*/
public function is_available(): bool {
return (bool) call_user_func( [ get_class( $this->importer ), 'is_active' ] );
}

/**
* {@inheritDoc}
*/
public function scan(): array {
if ( ! $this->is_available() ) {
return [];
Expand All @@ -59,6 +90,13 @@ public function scan(): array {
return $snippets;
}

/**
* Merge per-row field overrides on top of adapter-wide defaults.
*
* @param array<string, mixed> $fields Row-specific overrides from {@see self::map_row()}.
*
* @return array<string, mixed>
*/
private function with_defaults( array $fields ): array {
return array_merge(
[
Expand Down
27 changes: 18 additions & 9 deletions src/php/UnifiedSnippets/Model/Discovered_Snippet.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,14 @@ class Discovered_Snippet extends Model {
private const VALID_TYPES = [ 'php', 'css', 'js', 'html', 'config', 'mixed' ];

private const VALID_SOURCE_TYPES = [
'theme', 'child-theme', 'plugin', 'builder',
'core', 'server', 'customizer', 'mu-plugin',
'theme',
'child-theme',
'plugin',
'builder',
'core',
'server',
'customizer',
'mu-plugin',
];

private const VALID_RISK_LEVELS = [ 'low', 'medium', 'high' ];
Expand Down Expand Up @@ -146,13 +152,16 @@ protected function prepare_risk_level( string $risk_level ): string {
* @return string SHA-256 hash.
*/
public function generate_hash(): string {
$key = implode( '|', [
$this->scanner_id,
$this->source_type,
$this->source_path,
$this->line_start,
$this->line_end,
] );
$key = implode(
'|',
[
$this->scanner_id,
$this->source_type,
$this->source_path,
$this->line_start,
$this->line_end,
]
);

$this->hash = hash( 'sha256', $key );

Expand Down
56 changes: 34 additions & 22 deletions src/php/UnifiedSnippets/REST/Scan_REST_Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,15 @@ class Scan_REST_Controller extends REST_Controller {
public const BASE_ROUTE = 'scan';

/**
* Registry of available scanners, used to look up and run scanners by ID.
*
* @var Scanner_Registry
*/
private Scanner_Registry $registry;

/**
* Persisted scan results store, used to save new scans and read prior results.
*
* @var Scan_Results_Store
*/
private Scan_Results_Store $store;
Expand Down Expand Up @@ -236,10 +240,12 @@ public function run_single_scanner( WP_REST_Request $request ) {

$this->store->merge_scanner_results( $scanner_id, $snippets );

return rest_ensure_response( [
'scanner_id' => $scanner_id,
'count' => count( $snippets ),
] );
return rest_ensure_response(
[
'scanner_id' => $scanner_id,
'count' => count( $snippets ),
]
);
}

/**
Expand All @@ -263,16 +269,18 @@ public function get_results( WP_REST_Request $request ): WP_REST_Response {

$metadata = $this->store->get_metadata();

return rest_ensure_response( [
'scan_date' => $metadata['scan_date'],
'scanners' => $metadata['scanners'],
'total_count' => $metadata['total_count'],
'count' => count( $snippets ),
'snippets' => array_map(
static fn( $s ) => $s->to_array(),
array_values( $snippets )
),
] );
return rest_ensure_response(
[
'scan_date' => $metadata['scan_date'],
'scanners' => $metadata['scanners'],
'total_count' => $metadata['total_count'],
'count' => count( $snippets ),
'snippets' => array_map(
static fn( $s ) => $s->to_array(),
array_values( $snippets )
),
]
);
}

/**
Expand All @@ -281,9 +289,11 @@ public function get_results( WP_REST_Request $request ): WP_REST_Response {
* @return WP_REST_Response
*/
public function list_scanners(): WP_REST_Response {
return rest_ensure_response( [
'scanners' => $this->registry->get_scanner_info(),
] );
return rest_ensure_response(
[
'scanners' => $this->registry->get_scanner_info(),
]
);
}

/**
Expand All @@ -301,11 +311,13 @@ public function get_changes(): WP_REST_Response {
$items
);

return rest_ensure_response( [
'new' => $serialize( $changes['new'] ),
'modified' => $serialize( $changes['modified'] ),
'removed' => $serialize( $changes['removed'] ),
] );
return rest_ensure_response(
[
'new' => $serialize( $changes['new'] ),
'modified' => $serialize( $changes['modified'] ),
'removed' => $serialize( $changes['removed'] ),
]
);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,39 @@
*/
class Header_Footer_Code_Manager_Scanner extends DB_Scanner_Adapter {

/**
* {@inheritDoc}
*/
public function get_id(): string {
return 'hfcm';
}

/**
* {@inheritDoc}
*/
public function get_label(): string {
return __( 'Header Footer Code Manager', 'code-snippets' );
}

/**
* {@inheritDoc}
*/
protected function create_importer(): Plugin_Importer {
return new Header_Footer_Code_Manager_Plugin_Importer();
}

/**
* {@inheritDoc}
*/
protected function get_table_name(): string {
return 'hfcm_scripts';
}

/**
* {@inheritDoc}
*
* @param array<string, mixed> $row Raw HFCM row from `{prefix}hfcm_scripts`.
*/
protected function map_row( array $row ): ?array {
$code = (string) ( $row['snippet'] ?? '' );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,39 @@ class Insert_Headers_And_Footers_Scanner extends DB_Scanner_Adapter {

private const SUPPORTED_CODE_TYPES = [ 'php', 'css', 'js', 'html', 'universal' ];

/**
* {@inheritDoc}
*/
public function get_id(): string {
return 'wpcode';
}

/**
* {@inheritDoc}
*/
public function get_label(): string {
return __( 'WPCode (Insert Headers and Footers)', 'code-snippets' );
}

/**
* {@inheritDoc}
*/
protected function create_importer(): Plugin_Importer {
return new Insert_Headers_And_Footers_Plugin_Importer();
}

/**
* {@inheritDoc}
*/
protected function get_table_name(): string {
return 'wpcode_snippets';
}

/**
* {@inheritDoc}
*
* @param array<string, mixed> $row Raw WPCode row from `wpcode` custom posts.
*/
protected function map_row( array $row ): ?array {
$code_type = (string) ( $row['code_type'] ?? '' );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,48 @@
*/
class Insert_PHP_Code_Snippet_Scanner extends DB_Scanner_Adapter {

/**
* {@inheritDoc}
*/
public function get_id(): string {
return 'insert-php-code-snippet';
}

/**
* {@inheritDoc}
*/
public function get_label(): string {
return __( 'Insert PHP Code Snippet', 'code-snippets' );
}

/**
* {@inheritDoc}
*
* Discovered snippets execute arbitrary PHP, so the default risk is raised to 'high'.
*/
public function get_risk_level(): string {
return 'high';
}

/**
* {@inheritDoc}
*/
protected function create_importer(): Plugin_Importer {
return new Insert_PHP_Code_Snippet_Plugin_Importer();
}

/**
* {@inheritDoc}
*/
protected function get_table_name(): string {
return 'xyz_ips_short_code';
}

/**
* {@inheritDoc}
*
* @param array<string, mixed> $row Raw Insert PHP Code Snippet row from `{prefix}xyz_ips_short_code`.
*/
protected function map_row( array $row ): ?array {
$code = (string) ( $row['content'] ?? '' );

Expand Down
25 changes: 25 additions & 0 deletions tests/phpunit/fakes/Fake_Hfcm_Importer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,37 @@

use Code_Snippets\REST_API\Import\Plugins\Header_Footer_Code_Manager_Plugin_Importer;

/**
* Test double for {@see Header_Footer_Code_Manager_Plugin_Importer}.
*
* Always reports the source plugin as active and returns whatever rows the test assigns to
* the public {@see self::$rows} property, so HFCM scanner tests do not need a real install.
*/
class Fake_Hfcm_Importer extends Header_Footer_Code_Manager_Plugin_Importer {

/**
* Rows the fake importer should return from {@see self::get_data()}.
*
* @var array<int, array<string, mixed>>
*/
public array $rows = [];

/**
* Force the source plugin to look active.
*
* @return bool Always true.
*/
public static function is_active(): bool {
return true;
}

/**
* Return the rows assigned to this fake.
*
* @param array<int|string> $ids_to_import Unused; satisfies the parent signature.
*
* @return array<int, array<string, mixed>>
*/
public function get_data( array $ids_to_import = [] ): array {
return $this->rows;
}
Expand Down
Loading
Loading