mirror of
https://github.com/Heretek-AI/heretek-control-core.git
synced 2026-10-12 02:03:55 +00:00
Sweeps shipped copy clean of em dashes: readme.txt, README.md, CHANGELOG.md, the bundled sample prompts, and user-facing PHP strings (tool descriptions, admin labels). PHP was swept on non-comment lines only, so developer docblocks and inline comments are untouched. Internal dev docs (CLAUDE.md, docs/) were deliberately left alone. Replacements are contextual: colons for label/definition patterns, commas for asides, en dashes for numeric ranges. The .mcpb bundle display_name now reads "EMCP Tools - <host>" with a plain hyphen; its test is updated to match. Fixing CHANGELOG.md also clears the em dashes from the website's generated changelog on the next site build, since that file is its source. Verified: php -l clean on every touched file, pro suite 1559 pass, public suite 102 pass. Bumps the pro submodule for the Pro-side sweep.
105 lines
3.4 KiB
PHP
105 lines
3.4 KiB
PHP
<?php
|
|
/**
|
|
* Core file integrity audit against official wordpress.org checksums.
|
|
*
|
|
* diff() is pure (unit-tested). run() fetches checksums + hashes core files
|
|
* (verified live) and degrades gracefully when the checksum API is unreachable.
|
|
*
|
|
* @package EMCP_Tools
|
|
* @since 3.0.0
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* @since 3.0.0
|
|
*/
|
|
class EMCP_Tools_Security_Integrity_Audit {
|
|
|
|
/**
|
|
* Pure: compare a checksum manifest against actual file hashes.
|
|
*
|
|
* @param array<string,string> $checksums core-relative path => expected md5.
|
|
* @param callable $hasher fn(string $relpath): ?string actual md5 (null = missing).
|
|
* @return array Finding[]
|
|
*/
|
|
public function diff( array $checksums, callable $hasher ): array {
|
|
$findings = array();
|
|
foreach ( $checksums as $path => $expected ) {
|
|
$actual = $hasher( (string) $path );
|
|
if ( null === $actual ) {
|
|
$findings[] = EMCP_Tools_Security_Finding::make(
|
|
'integrity_missing', 'integrity', 'Missing core file', 'warning', (string) $path,
|
|
sprintf( 'Core file %s is missing.', $path ),
|
|
'Reinstall WordPress core (Dashboard → Updates → Re-install) to restore missing files.'
|
|
);
|
|
continue;
|
|
}
|
|
if ( ! hash_equals( strtolower( (string) $expected ), strtolower( (string) $actual ) ) ) {
|
|
$findings[] = EMCP_Tools_Security_Finding::make(
|
|
'integrity_modified', 'integrity', 'Modified core file', 'critical', (string) $path,
|
|
sprintf( 'Core file %s does not match the official checksum.', $path ),
|
|
'A modified core file is a strong infection signal. Re-install WordPress core and investigate how it changed.'
|
|
);
|
|
}
|
|
}
|
|
return $findings;
|
|
}
|
|
|
|
/**
|
|
* Live: fetch checksums + hash core files. Excludes wp-content (not part of
|
|
* core checksums).
|
|
*
|
|
* @return array { findings: Finding[], api: array{ ok: bool, error: ?string } }
|
|
*/
|
|
public function run(): array {
|
|
if ( ! function_exists( 'get_core_checksums' ) ) {
|
|
require_once ABSPATH . 'wp-admin/includes/update.php';
|
|
}
|
|
global $wp_version;
|
|
$locale = function_exists( 'get_locale' ) ? get_locale() : 'en_US';
|
|
$checksums = function_exists( 'get_core_checksums' ) ? get_core_checksums( $wp_version, $locale ) : false;
|
|
|
|
if ( empty( $checksums ) || ! is_array( $checksums ) ) {
|
|
return array(
|
|
'findings' => array(
|
|
EMCP_Tools_Security_Finding::make(
|
|
'integrity_unavailable', 'integrity', 'Core checksums', 'info', false,
|
|
'Could not retrieve official core checksums (offline or wordpress.org unreachable).',
|
|
'Run this scan on an internet-connected environment to verify core file integrity.'
|
|
),
|
|
),
|
|
'api' => array( 'ok' => false, 'error' => 'checksums_unavailable' ),
|
|
);
|
|
}
|
|
|
|
$hasher = static function ( string $relpath ): ?string {
|
|
$full = ABSPATH . $relpath;
|
|
if ( 0 === strpos( $relpath, 'wp-content/' ) ) {
|
|
return null; // Excluded, treat as present to suppress findings.
|
|
}
|
|
if ( ! is_file( $full ) ) {
|
|
return null;
|
|
}
|
|
$md5 = @md5_file( $full );
|
|
return false === $md5 ? null : $md5;
|
|
};
|
|
|
|
// Skip wp-content entries entirely (excluded from integrity).
|
|
$filtered = array();
|
|
foreach ( $checksums as $path => $md5 ) {
|
|
if ( 0 === strpos( (string) $path, 'wp-content/' ) ) {
|
|
continue;
|
|
}
|
|
$filtered[ $path ] = $md5;
|
|
}
|
|
|
|
return array(
|
|
'findings' => $this->diff( $filtered, $hasher ),
|
|
'api' => array( 'ok' => true, 'error' => null ),
|
|
);
|
|
}
|
|
}
|