callbacks ) { $out[] = Finding::make( 'cron_no_callback', $e->hook, array( $e->hook ) ); } foreach ( $e->callbacks as $cb ) { $file = $cb['file']; if ( null === $file ) { continue; } $args = array( $e->hook, $cb['function'], $file ); if ( ! $cb['inside'] ) { $out[] = Finding::make( 'cron_callback_outside', $e->hook, $args ); } elseif ( Context::under( $file, $ctx->uploadsDir ) ) { $out[] = Finding::make( 'cron_callback_uploads', $e->hook, $args ); } elseif ( Context::under( $file, $ctx->muPluginsDir ) && ! in_array( $file, $ctx->knownMuPlugins, true ) ) { $out[] = Finding::make( 'cron_callback_mu_plugin', $e->hook, $args ); } else { $origin = self::origin( $file, $ctx ); if ( '' !== $origin ) { $out[] = Finding::make( 'cron_attributed', $e->hook, array( $e->hook, $origin ) ); } } } $hit = Text::firstMarker( $e->args, Assumptions::OBF ); if ( null !== $hit ) { $out[] = Finding::make( 'cron_args_marker', $e->hook, array( $e->hook, $hit[0] ), Text::excerptAround( $e->args, $hit[1] ) ); } return $out; } /** 'core', 'plugin ' (active), 'theme', or '' when none of them. */ private static function origin( string $file, Context $ctx ): string { if ( false === strpos( $file, '/' ) || Text::startsWith( $file, 'wp-admin/' ) || Text::startsWith( $file, 'wp-includes/' ) ) { return 'core'; } foreach ( $ctx->plugins as $p ) { if ( ! $p['active'] ) { continue; } $isDir = false !== strpos( $p['file'], '/' ); $base = $ctx->pluginsDir . '/' . ( $isDir ? Context::slugOf( $p['file'] ) : $p['file'] ); if ( $isDir ? Context::under( $file, $base ) : $file === $base ) { return 'plugin ' . Context::slugOf( $p['file'] ); } } foreach ( $ctx->activeThemeDirs as $dir ) { if ( Context::under( $file, $dir ) ) { return 'theme'; } } return ''; } }