|
8 | 8 | "inputs": { |
9 | 9 | "mounts": [ |
10 | 10 | { |
11 | | - "source": "../../tests/fixtures/wordpress-adversarial/vulnerable-plugin", |
12 | | - "target": "/wordpress/wp-content/plugins/wp-codebox-adversarial-vulnerable", |
| 11 | + "source": "../../tests/fixtures/wordpress-adversarial/vulnerable-plugin/vulnerable-plugin.php", |
| 12 | + "target": "/wordpress/wp-content/mu-plugins/wp-codebox-adversarial-vulnerable.php", |
| 13 | + "type": "file", |
13 | 14 | "mode": "readonly", |
14 | 15 | "metadata": { "kind": "fixture", "disposableOnly": true } |
15 | 16 | }, |
|
25 | 26 | "steps": [ |
26 | 27 | { |
27 | 28 | "command": "wordpress.run-php", |
28 | | - "args": ["code=require_once ABSPATH . 'wp-admin/includes/plugin.php'; activate_plugin('wp-codebox-adversarial-vulnerable/vulnerable-plugin.php'); switch_theme('wp-codebox-adversarial-vulnerable'); delete_option('wp_codebox_vulnerable_authorization'); delete_option('wp_codebox_vulnerable_partial_state');"] |
| 29 | + "args": ["code=switch_theme('wp-codebox-adversarial-vulnerable'); foreach (array('wp_codebox_vulnerable_authorization', 'wp_codebox_vulnerable_partial_state', 'wp_codebox_vulnerable_ajax', 'wp_codebox_vulnerable_xmlrpc', 'wp_codebox_vulnerable_injection', 'wp_codebox_vulnerable_race_count', 'wp_codebox_adversarial_http_faults', 'wp_codebox_adversarial_http_fault_counts') as $option) { delete_option($option); }"] |
29 | 30 | } |
30 | 31 | ] |
31 | 32 | }, |
|
86 | 87 | "shrinking": { "enabled": true }, |
87 | 88 | "replay": { "selection": "findings" }, |
88 | 89 | "metadata": { "fixtureDefect": "state-corruption", "network": "deny" } |
| 90 | + }, |
| 91 | + { |
| 92 | + "schema": "wp-codebox/adversarial-recipe-campaign/v1", |
| 93 | + "id": "wordpress-vulnerable-ajax", |
| 94 | + "seed": "wordpress-vulnerable-ajax-v1", |
| 95 | + "corpus": [ |
| 96 | + { "id": "unauthenticated-ajax", "actions": [{ "type": "ajax-save", "input": { "value": "fixture-value" } }], "signals": ["transport:ajax"] } |
| 97 | + ], |
| 98 | + "caseTemplates": [ |
| 99 | + { |
| 100 | + "id": "ajax-save", |
| 101 | + "phases": { |
| 102 | + "action": [{ "command": "wordpress.browser-actions", "args": ["steps-json=[{\"kind\":\"navigate\",\"url\":\"/\",\"waitFor\":\"load\"},{\"kind\":\"evaluate\",\"expression\":\"const response = await fetch('/wp-admin/admin-ajax.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: 'action=wp_codebox_vulnerable_save&value=fixture-value' }); return response.ok\",\"assert\":true}]", "capture=steps,network", "timeout=30s"] }], |
| 103 | + "assert": [{ "command": "wordpress.run-php", "args": ["code=if (get_option('wp_codebox_vulnerable_ajax', null) !== null) { throw new RuntimeException('ajax-nonce-bypass'); }"] }], |
| 104 | + "teardown": [{ "command": "wordpress.run-php", "args": ["code=delete_option('wp_codebox_vulnerable_ajax');"] }] |
| 105 | + } |
| 106 | + } |
| 107 | + ], |
| 108 | + "mutators": ["scalar"], |
| 109 | + "oracles": [{ "id": "runtime-status", "severity": "critical", "description": "Unauthenticated AJAX writes without a nonce must fail closed." }], |
| 110 | + "concurrency": 1, |
| 111 | + "budgets": { "maxCases": 1, "maxActionsPerCase": 2, "maxInputBytes": 4096, "maxCaseTimeMs": 15000, "maxWallTimeMs": 120000, "maxArtifactBytes": 10485760 }, |
| 112 | + "resetPolicy": { "mode": "none" }, |
| 113 | + "requiredCapabilities": ["adversarial-campaign", "artifact-export", "bounded-evidence", "replay", "command:wordpress.browser-actions", "command:wordpress.run-php"], |
| 114 | + "novelty": { "retainSignals": true }, |
| 115 | + "shrinking": { "enabled": false }, |
| 116 | + "replay": { "selection": "findings" }, |
| 117 | + "metadata": { "fixtureDefect": "ajax-nonce-bypass", "transportFidelity": "exact", "network": "deny" } |
| 118 | + }, |
| 119 | + { |
| 120 | + "schema": "wp-codebox/adversarial-recipe-campaign/v1", |
| 121 | + "id": "wordpress-vulnerable-xmlrpc", |
| 122 | + "seed": "wordpress-vulnerable-xmlrpc-v1", |
| 123 | + "corpus": [ |
| 124 | + { "id": "unauthenticated-xmlrpc", "actions": [{ "type": "xmlrpc-save", "input": {} }], "signals": ["transport:xmlrpc"] } |
| 125 | + ], |
| 126 | + "caseTemplates": [ |
| 127 | + { |
| 128 | + "id": "xmlrpc-save", |
| 129 | + "phases": { |
| 130 | + "action": [{ "command": "wordpress.browser-actions", "args": ["steps-json=[{\"kind\":\"navigate\",\"url\":\"/\",\"waitFor\":\"load\"},{\"kind\":\"evaluate\",\"expression\":\"const response = await fetch('/xmlrpc.php', { method: 'POST', headers: { 'Content-Type': 'text/xml' }, body: '<?xml version=\\\"1.0\\\"?><methodCall><methodName>wpCodebox.vulnerable</methodName><params><param><value><string>fixture-value</string></value></param></params></methodCall>' }); return response.ok\",\"assert\":true}]", "capture=steps,network", "timeout=30s"] }], |
| 131 | + "assert": [{ "command": "wordpress.run-php", "args": ["code=if (get_option('wp_codebox_vulnerable_xmlrpc', null) !== null) { throw new RuntimeException('xmlrpc-authorization-bypass'); }"] }], |
| 132 | + "teardown": [{ "command": "wordpress.run-php", "args": ["code=delete_option('wp_codebox_vulnerable_xmlrpc');"] }] |
| 133 | + } |
| 134 | + } |
| 135 | + ], |
| 136 | + "mutators": ["scalar"], |
| 137 | + "oracles": [{ "id": "runtime-status", "severity": "critical", "description": "Unauthenticated XML-RPC writes must fail closed." }], |
| 138 | + "concurrency": 1, |
| 139 | + "budgets": { "maxCases": 1, "maxActionsPerCase": 2, "maxInputBytes": 4096, "maxCaseTimeMs": 15000, "maxWallTimeMs": 120000, "maxArtifactBytes": 10485760 }, |
| 140 | + "resetPolicy": { "mode": "none" }, |
| 141 | + "requiredCapabilities": ["adversarial-campaign", "artifact-export", "bounded-evidence", "replay", "command:wordpress.browser-actions", "command:wordpress.run-php"], |
| 142 | + "novelty": { "retainSignals": true }, |
| 143 | + "shrinking": { "enabled": false }, |
| 144 | + "replay": { "selection": "findings" }, |
| 145 | + "metadata": { "fixtureDefect": "xmlrpc-authorization-bypass", "transportFidelity": "exact", "network": "deny" } |
| 146 | + }, |
| 147 | + { |
| 148 | + "schema": "wp-codebox/adversarial-recipe-campaign/v1", |
| 149 | + "id": "wordpress-vulnerable-injection", |
| 150 | + "seed": "wordpress-vulnerable-injection-v1", |
| 151 | + "corpus": [ |
| 152 | + { "id": "rest-code-injection", "actions": [{ "type": "injection-rest", "input": { "expression": "update_option('wp_codebox_vulnerable_injection', 'executed')" } }], "signals": ["route:injection"] } |
| 153 | + ], |
| 154 | + "caseTemplates": [ |
| 155 | + { |
| 156 | + "id": "injection-rest", |
| 157 | + "phases": { |
| 158 | + "action": [{ "command": "wordpress.run-php", "args": ["code=$request = new WP_REST_Request('POST', '/wp-codebox-vulnerable/v1/injection'); $request->set_body_params(json_decode(base64_decode('{{action.inputBase64}}'), true)); rest_do_request($request); if (get_option('wp_codebox_vulnerable_injection', null) !== null) { throw new RuntimeException('code-execution-indicator'); }"] }], |
| 159 | + "assert": [{ "command": "wordpress.run-php", "args": ["code=if (get_option('wp_codebox_vulnerable_injection', null) !== null) { throw new RuntimeException('code-execution-indicator'); }"] }], |
| 160 | + "teardown": [{ "command": "wordpress.run-php", "args": ["code=delete_option('wp_codebox_vulnerable_injection');"] }] |
| 161 | + } |
| 162 | + } |
| 163 | + ], |
| 164 | + "mutators": ["sequence"], |
| 165 | + "oracles": [{ "id": "runtime-status", "severity": "critical", "description": "Mutated REST input must not execute server-side code." }], |
| 166 | + "concurrency": 1, |
| 167 | + "budgets": { "maxCases": 2, "maxActionsPerCase": 3, "maxInputBytes": 4096, "maxCaseTimeMs": 15000, "maxWallTimeMs": 120000, "maxArtifactBytes": 10485760 }, |
| 168 | + "resetPolicy": { "mode": "checkpoint-per-case", "checkpointName": "wordpress-vulnerable-injection-baseline" }, |
| 169 | + "requiredCapabilities": ["adversarial-campaign", "artifact-export", "bounded-evidence", "replay", "command:wordpress.rest-request", "command:wordpress.run-php"], |
| 170 | + "novelty": { "retainSignals": true }, |
| 171 | + "shrinking": { "enabled": true }, |
| 172 | + "replay": { "selection": "findings" }, |
| 173 | + "metadata": { "fixtureDefect": "injection", "network": "deny" } |
| 174 | + }, |
| 175 | + { |
| 176 | + "schema": "wp-codebox/adversarial-recipe-campaign/v1", |
| 177 | + "id": "wordpress-vulnerable-external-service", |
| 178 | + "seed": "wordpress-vulnerable-external-service-v1", |
| 179 | + "corpus": [ |
| 180 | + { "id": "failure-then-recovery", "actions": [{ "type": "external-service-cycle", "input": {} }], "signals": ["transport:failure", "transport:recovery"] } |
| 181 | + ], |
| 182 | + "caseTemplates": [ |
| 183 | + { |
| 184 | + "id": "external-service-cycle", |
| 185 | + "phases": { |
| 186 | + "setup": [{ "command": "wordpress.run-php", "args": ["code=delete_option('wp_codebox_adversarial_http_fault_counts'); update_option('wp_codebox_adversarial_http_faults', array('schema' => 'wp-codebox/transport-fault-model/v1', 'seed' => 'failure-recovery', 'rules' => array(array('id' => 'fixture-cycle', 'sequence' => array(array('timeoutMs' => 10), array('status' => 403, 'body' => '{\"authorized\":false}'))))), false);"] }], |
| 187 | + "action": [{ "command": "wordpress.run-php", "args": ["code=$request = new WP_REST_Request('POST', '/wp-codebox-vulnerable/v1/external-service'); $failed = rest_do_request($request); $recovered = rest_do_request($request); $failed_data = (array) $failed->get_data(); $recovered_data = (array) $recovered->get_data(); if (!empty($recovered_data['authorized'])) { throw new RuntimeException('external-service-recovery-failed'); } if (!empty($failed_data['authorized'])) { throw new RuntimeException('external-service-fail-open'); }"] }], |
| 188 | + "teardown": [{ "command": "wordpress.run-php", "args": ["code=delete_option('wp_codebox_adversarial_http_faults'); delete_option('wp_codebox_adversarial_http_fault_counts');"] }] |
| 189 | + } |
| 190 | + } |
| 191 | + ], |
| 192 | + "mutators": ["sequence"], |
| 193 | + "oracles": [{ "id": "runtime-status", "severity": "high", "description": "Dependency failure must fail closed and the next healthy response must recover." }], |
| 194 | + "concurrency": 1, |
| 195 | + "budgets": { "maxCases": 1, "maxActionsPerCase": 2, "maxInputBytes": 4096, "maxCaseTimeMs": 15000, "maxWallTimeMs": 120000, "maxArtifactBytes": 10485760 }, |
| 196 | + "resetPolicy": { "mode": "checkpoint-per-case", "checkpointName": "wordpress-vulnerable-external-baseline" }, |
| 197 | + "requiredCapabilities": ["adversarial-campaign", "artifact-export", "bounded-evidence", "replay", "command:wordpress.run-php"], |
| 198 | + "optionalCapabilities": ["transport-faults"], |
| 199 | + "novelty": { "retainSignals": true }, |
| 200 | + "shrinking": { "enabled": true }, |
| 201 | + "replay": { "selection": "findings" }, |
| 202 | + "metadata": { "fixtureDefect": "external-service-fail-open", "network": "deny", "recoveryVerified": true } |
| 203 | + }, |
| 204 | + { |
| 205 | + "schema": "wp-codebox/adversarial-recipe-campaign/v1", |
| 206 | + "id": "wordpress-vulnerable-ui-duplicate", |
| 207 | + "seed": "wordpress-vulnerable-ui-duplicate-v1", |
| 208 | + "corpus": [ |
| 209 | + { "id": "admin-duplicate-effect", "actions": [{ "type": "browser-journey", "input": [{ "kind": "navigate", "url": "/wp-admin/tools.php?page=wp-codebox-vulnerable", "waitFor": "load" }, { "kind": "click", "selector": ".wp-codebox-vulnerable-save" }, { "kind": "evaluate", "expression": "document.querySelector('.wp-codebox-vulnerable-count').value", "assert": "1" }, { "kind": "capture" }, { "kind": "capture" }, { "kind": "capture" }] }], "signals": ["browser:duplicate-effect"] } |
| 210 | + ], |
| 211 | + "caseTemplates": [ |
| 212 | + { "id": "browser-journey", "phases": { "action": [{ "command": "wordpress.browser-actions", "args": ["steps-json={{action.input}}", "auth=wordpress-admin", "auth-user-id=1", "capture=steps,html,screenshot,dom-snapshot", "timeout=30s"] }] } } |
| 213 | + ], |
| 214 | + "mutators": ["sequence"], |
| 215 | + "oracles": [{ "id": "runtime-status", "severity": "high", "description": "One UI action must produce at most one effect." }], |
| 216 | + "concurrency": 1, |
| 217 | + "budgets": { "maxCases": 1, "maxActionsPerCase": 2, "maxInputBytes": 16384, "maxCaseTimeMs": 45000, "maxWallTimeMs": 120000, "maxArtifactBytes": 20971520 }, |
| 218 | + "resetPolicy": { "mode": "checkpoint-per-case", "checkpointName": "wordpress-vulnerable-ui-duplicate-baseline" }, |
| 219 | + "requiredCapabilities": ["adversarial-campaign", "artifact-export", "bounded-evidence", "replay", "command:wordpress.browser-actions"], |
| 220 | + "novelty": { "retainSignals": true }, |
| 221 | + "shrinking": { "enabled": true }, |
| 222 | + "replay": { "selection": "findings" }, |
| 223 | + "metadata": { "fixtureDefect": "duplicated-ui-effect", "network": "deny", "browserJourney": true } |
| 224 | + }, |
| 225 | + { |
| 226 | + "schema": "wp-codebox/adversarial-recipe-campaign/v1", |
| 227 | + "id": "wordpress-vulnerable-ui-loading", |
| 228 | + "seed": "wordpress-vulnerable-ui-loading-v1", |
| 229 | + "corpus": [ |
| 230 | + { "id": "admin-stuck-loading", "actions": [{ "type": "browser-journey", "input": [{ "kind": "navigate", "url": "/wp-admin/tools.php?page=wp-codebox-vulnerable", "waitFor": "load" }, { "kind": "click", "selector": ".wp-codebox-vulnerable-save" }, { "kind": "evaluate", "expression": "document.querySelector('.spinner').classList.contains('is-active')", "assert": false }, { "kind": "capture" }, { "kind": "capture" }, { "kind": "capture" }] }], "signals": ["browser:stuck-loading"] } |
| 231 | + ], |
| 232 | + "caseTemplates": [ |
| 233 | + { "id": "browser-journey", "phases": { "action": [{ "command": "wordpress.browser-actions", "args": ["steps-json={{action.input}}", "auth=wordpress-admin", "auth-user-id=1", "capture=steps,html,screenshot,dom-snapshot", "timeout=30s"] }] } } |
| 234 | + ], |
| 235 | + "mutators": ["sequence"], |
| 236 | + "oracles": [{ "id": "runtime-status", "severity": "high", "description": "Loading state must clear after the UI action completes." }], |
| 237 | + "concurrency": 1, |
| 238 | + "budgets": { "maxCases": 1, "maxActionsPerCase": 2, "maxInputBytes": 16384, "maxCaseTimeMs": 45000, "maxWallTimeMs": 120000, "maxArtifactBytes": 20971520 }, |
| 239 | + "resetPolicy": { "mode": "checkpoint-per-case", "checkpointName": "wordpress-vulnerable-ui-loading-baseline" }, |
| 240 | + "requiredCapabilities": ["adversarial-campaign", "artifact-export", "bounded-evidence", "replay", "command:wordpress.browser-actions"], |
| 241 | + "novelty": { "retainSignals": true }, |
| 242 | + "shrinking": { "enabled": true }, |
| 243 | + "replay": { "selection": "findings" }, |
| 244 | + "metadata": { "fixtureDefect": "stuck-loading-state", "network": "deny", "browserJourney": true } |
89 | 245 | } |
90 | 246 | ], |
91 | 247 | "artifacts": { |
|
0 commit comments