path ) { // The marker check needs no checksums: it runs even when control 1 has none. return self::wpConfig( $f, $ctx ); } $sums = $ctx->coreChecksums; if ( null === $sums ) { return array(); } if ( $sums->isFallback() && in_array( $f->path, Assumptions::LOCALE_DEPENDENT_FILES, true ) ) { return array(); } $args = array( $f->path, $sums->version, $sums->locale ); if ( isset( $sums->sums[ $f->path ] ) ) { if ( null !== $f->md5 && $f->md5 !== $sums->sums[ $f->path ] ) { return array( Finding::make( 'core_modified', $f->path, $args ) ); } return array(); } if ( Text::startsWith( $f->path, 'wp-admin/' ) || Text::startsWith( $f->path, 'wp-includes/' ) ) { return array( Finding::make( 'core_unknown_file', $f->path, $args ) ); } if ( false === strpos( $f->path, '/' ) && Text::isPhpFile( $f->path ) ) { // Firewall file of an installed plugin (DECISIONS 2026-10-24, D-02, D-03). $known = Assumptions::KNOWN_PREPEND_TARGETS; if ( isset( $known[ $f->path ] ) && in_array( $known[ $f->path ], $ctx->installedSlugs(), true ) ) { return array( Finding::make( 'core_root_plugin_file', $f->path, array( $f->path, $known[ $f->path ] ) ) ); } return array( Finding::make( 'core_root_unknown_php', $f->path, $args ) ); } return array(); } /** @return Finding[] */ private static function wpConfig( FileEntry $f, Context $ctx ): array { $text = $f->text(); $out = array(); // The exact block of the official Docker image: to identify, and its eval( is // blanked (same length, so excerpts stay in place) before the marker check. $lf = str_replace( "\r\n", "\n", $text ); $block = Assumptions::DOCKER_CONFIG_EXTRA; $at = strpos( $lf, $block ); if ( false !== $at ) { $text = substr_replace( $lf, str_repeat( ' ', strlen( $block ) ), $at, strlen( $block ) ); $out[] = Finding::make( 'config_docker_extra', $f->path, array(), $block ); } foreach ( Assumptions::WP_CONFIG_MARKERS as $marker ) { $pos = stripos( $text, $marker ); // Call markers also with 1 to 10 blanks before the parenthesis (D-12). if ( false === $pos && Text::endsWith( $marker, '(' ) ) { $spaced = Patterns::spacedCall( $text, substr( $marker, 0, -1 ) ); $pos = null === $spaced ? false : $spaced; } if ( false !== $pos ) { $out[] = Finding::make( 'config_marker', $f->path, array( $marker ), Text::excerptAround( $text, $pos ) ); } } foreach ( Patterns::includeStatements( $text ) as $statement ) { $shown = Text::cut( $statement, Assumptions::EXCERPT_MAX_CHARS ); if ( self::pointsToUploads( $statement, $ctx ) ) { $out[] = Finding::make( 'config_uploads_include', $f->path, array( $shown ), $shown ); } elseif ( Patterns::hasEscapedPath( $statement ) ) { // Path written in \0nn or \xnn escapes (D-12, "favicon .ico" infections). $out[] = Finding::make( 'config_escaped_include', $f->path, array( $shown ), $shown ); } } return $out; } private static function pointsToUploads( string $statement, Context $ctx ): bool { if ( false !== stripos( $statement, $ctx->uploadsDir . '/' ) || false !== stripos( $statement, 'wp-content/uploads/' ) ) { return true; } // WP_CONTENT_DIR . '/uploads/...' names the same folder. return false !== stripos( $statement, 'WP_CONTENT_DIR' ) && false !== stripos( $statement, '/uploads/' ); } }