__( 'Security Scan', 'agentic-admin' ), 'description' => __( 'Run basic WordPress security checks.', 'agentic-admin' ), 'category' => 'sre-tools', 'input_schema' => array( 'type' => 'object', 'default' => array(), 'properties' => array(), 'additionalProperties' => false, ), 'output_schema' => array( 'type' => 'object', 'properties' => array( 'checks' => array( 'type' => 'array', 'description' => __( 'List of security check results.', 'agentic-admin' ), ), 'summary' => array( 'type' => 'object', 'description' => __( 'Count by severity.', 'agentic-admin' ), ), ), ), 'execute_callback' => 'agentic_admin_execute_security_scan', 'permission_callback' => function () { return current_user_can( 'manage_options' ); }, 'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => true, 'destructive' => false, 'idempotent' => true, ), ), ), // JS configuration for chat interface. array( 'keywords' => array( 'security', 'secure', 'scan', 'vulnerability', 'hardening', 'permissions' ), 'initialMessage' => __( "I'll run a basic security scan...", 'agentic-admin' ), ) ); } /** * Execute the security-scan ability. * * @param array $input Input parameters. * @return array */ function agentic_admin_execute_security_scan( array $input = array() ): array { $checks = array(); // 1. Check WP_DEBUG in production. $checks[] = array( 'check' => 'Debug mode', 'status' => defined( 'WP_DEBUG' ) && WP_DEBUG ? 'fail' : 'pass', 'severity' => 'warning', 'message' => defined( 'WP_DEBUG' ) && WP_DEBUG ? 'WP_DEBUG is enabled. Disable in production.' : 'WP_DEBUG is disabled.', ); // 2. Check WP_DEBUG_DISPLAY. $checks[] = array( 'check' => 'Debug display', 'status' => defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG_DISPLAY ? 'fail' : 'pass', 'severity' => 'critical', 'message' => defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG_DISPLAY ? 'WP_DEBUG_DISPLAY is on. Errors are visible to visitors.' : 'WP_DEBUG_DISPLAY is off.', ); // 3. Check wp-config.php file permissions. $wp_config = agentic_admin_locate_wp_config(); if ( '' !== $wp_config ) { $perms = fileperms( $wp_config ) & 0777; $is_secure = $perms <= 0640; $checks[] = array( 'check' => 'wp-config.php permissions', 'status' => $is_secure ? 'pass' : 'fail', 'severity' => 'critical', 'message' => $is_secure ? sprintf( 'Permissions are %s (secure).', decoct( $perms ) ) : sprintf( 'Permissions are %s. Should be 640 or less.', decoct( $perms ) ), ); } // 4. Check WordPress version exposure. $checks[] = array( 'check' => 'Version in HTML', 'status' => has_action( 'wp_head', 'wp_generator' ) ? 'fail' : 'pass', 'severity' => 'info', 'message' => has_action( 'wp_head', 'wp_generator' ) ? 'WordPress version is exposed in page source.' : 'WordPress version is hidden.', ); // 5. Check directory listing. $uploads_dir = wp_upload_dir(); $uploads_path = $uploads_dir['basedir']; $htaccess = $uploads_path . '/.htaccess'; $index_file = $uploads_path . '/index.php'; $has_protect = file_exists( $htaccess ) || file_exists( $index_file ); $checks[] = array( 'check' => 'Uploads directory listing', 'status' => $has_protect ? 'pass' : 'fail', 'severity' => 'warning', 'message' => $has_protect ? 'Uploads directory has listing protection.' : 'Uploads directory may allow directory listing.', ); $vulnerabilities = agentic_admin_scan_for_vulnerabilities(); $checks[] = array( 'check' => 'Plugin vulnerabilities', 'status' => ! empty( $vulnerabilities['total_vulnerabilities'] ) ? 'fail' : 'pass', 'severity' => 'critical', 'message' => ! empty( $vulnerabilities['total_vulnerabilities'] ) ? sprintf( /* translators: 1: vulnerabilities count, 2: affected plugin count */ __( 'Detected %1$d known vulnerabilities across %2$d plugin(s).', 'agentic-admin' ), (int) $vulnerabilities['total_vulnerabilities'], (int) $vulnerabilities['plugins_with_issues'] ) : __( 'No known plugin vulnerabilities detected for scanned plugins.', 'agentic-admin' ), ); $summary = array( 'critical' => 0, 'warning' => 0, 'info' => 0, 'passed' => 0, 'failed' => 0, ); foreach ( $checks as $check ) { if ( 'fail' === $check['status'] ) { ++$summary['failed']; ++$summary[ $check['severity'] ]; } else { ++$summary['passed']; } } return array( 'checks' => $checks, 'summary' => $summary, 'vulnerabilities' => $vulnerabilities, ); }