Skip to content

Commit 2dfb11a

Browse files
authored
feat: add WordPress adversarial Playground adapter (#2028)
Adds a WordPress-owned adapter, explicit fidelity reporting, vulnerable disposable fixtures, and replayable authorization and state-corruption campaigns. Refs #2017.
1 parent 090a993 commit 2dfb11a

9 files changed

Lines changed: 614 additions & 1 deletion

File tree

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
{
2+
"schema": "wp-codebox/workspace-recipe/v1",
3+
"runtime": {
4+
"backend": "wordpress-playground",
5+
"name": "wordpress-adversarial-vulnerable",
6+
"wp": "latest"
7+
},
8+
"inputs": {
9+
"mounts": [
10+
{
11+
"source": "../../tests/fixtures/wordpress-adversarial/vulnerable-plugin",
12+
"target": "/wordpress/wp-content/plugins/wp-codebox-adversarial-vulnerable",
13+
"mode": "readonly",
14+
"metadata": { "kind": "fixture", "disposableOnly": true }
15+
},
16+
{
17+
"source": "../../tests/fixtures/wordpress-adversarial/vulnerable-theme",
18+
"target": "/wordpress/wp-content/themes/wp-codebox-adversarial-vulnerable",
19+
"mode": "readonly",
20+
"metadata": { "kind": "fixture", "disposableOnly": true }
21+
}
22+
]
23+
},
24+
"workflow": {
25+
"steps": [
26+
{
27+
"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+
}
30+
]
31+
},
32+
"adversarialCampaigns": [
33+
{
34+
"schema": "wp-codebox/adversarial-recipe-campaign/v1",
35+
"id": "wordpress-vulnerable-authorization",
36+
"seed": "wordpress-vulnerable-authorization-v1",
37+
"corpus": [
38+
{ "id": "unauthenticated-rest", "actions": [{ "type": "authorization-rest", "input": { "role": "subscriber", "secret": "fixture-secret" } }], "signals": ["route:authorization"] }
39+
],
40+
"caseTemplates": [
41+
{
42+
"id": "authorization-rest",
43+
"phases": {
44+
"action": [{ "command": "wordpress.rest-request", "args": ["method=POST", "path=/wp-codebox-vulnerable/v1/authorization", "body-json={{action.input}}", "capture-diagnostics=wpdb-queries"] }],
45+
"assert": [{ "command": "wordpress.run-php", "args": ["code=if (get_option('wp_codebox_vulnerable_authorization', null) !== null) { throw new RuntimeException('authorization-bypass'); }"] }],
46+
"teardown": [{ "command": "wordpress.run-php", "args": ["code=delete_option('wp_codebox_vulnerable_authorization');"] }]
47+
}
48+
}
49+
],
50+
"mutators": ["scalar", "structured", "sequence"],
51+
"oracles": [{ "id": "runtime-status", "severity": "critical", "description": "Unauthenticated state changes must fail closed." }],
52+
"concurrency": 1,
53+
"budgets": { "maxCases": 4, "maxActionsPerCase": 4, "maxInputBytes": 4096, "maxCaseTimeMs": 15000, "maxWallTimeMs": 120000, "maxArtifactBytes": 10485760 },
54+
"resetPolicy": { "mode": "checkpoint-per-case", "checkpointName": "wordpress-vulnerable-authorization-baseline" },
55+
"requiredCapabilities": ["adversarial-campaign", "artifact-export", "bounded-evidence", "replay", "command:wordpress.rest-request", "command:wordpress.run-php"],
56+
"optionalCapabilities": ["transport-faults"],
57+
"novelty": { "retainSignals": true },
58+
"shrinking": { "enabled": true },
59+
"replay": { "selection": "findings" },
60+
"metadata": { "fixtureDefect": "authorization", "network": "deny" }
61+
},
62+
{
63+
"schema": "wp-codebox/adversarial-recipe-campaign/v1",
64+
"id": "wordpress-vulnerable-state",
65+
"seed": "wordpress-vulnerable-state-v1",
66+
"corpus": [
67+
{ "id": "partial-state", "actions": [{ "type": "partial-state-rest", "input": {} }], "signals": ["route:partial-state"] }
68+
],
69+
"caseTemplates": [
70+
{
71+
"id": "partial-state-rest",
72+
"phases": {
73+
"action": [{ "command": "wordpress.rest-request", "args": ["method=POST", "path=/wp-codebox-vulnerable/v1/partial-state", "body-json={{action.input}}", "capture-diagnostics=wpdb-queries"] }],
74+
"assert": [{ "command": "wordpress.run-php", "args": ["code=if (get_option('wp_codebox_vulnerable_partial_state', null) !== null) { throw new RuntimeException('partial-state-commit'); }"] }],
75+
"teardown": [{ "command": "wordpress.run-php", "args": ["code=delete_option('wp_codebox_vulnerable_partial_state');"] }]
76+
}
77+
}
78+
],
79+
"mutators": ["scalar", "sequence"],
80+
"oracles": [{ "id": "runtime-status", "severity": "high", "description": "Failed requests must not commit partial state." }],
81+
"concurrency": 1,
82+
"budgets": { "maxCases": 3, "maxActionsPerCase": 3, "maxInputBytes": 4096, "maxCaseTimeMs": 15000, "maxWallTimeMs": 120000, "maxArtifactBytes": 10485760 },
83+
"resetPolicy": { "mode": "checkpoint-per-case", "checkpointName": "wordpress-vulnerable-state-baseline" },
84+
"requiredCapabilities": ["adversarial-campaign", "artifact-export", "bounded-evidence", "replay", "command:wordpress.rest-request", "command:wordpress.run-php"],
85+
"novelty": { "retainSignals": true },
86+
"shrinking": { "enabled": true },
87+
"replay": { "selection": "findings" },
88+
"metadata": { "fixtureDefect": "state-corruption", "network": "deny" }
89+
}
90+
],
91+
"artifacts": {
92+
"directory": "./artifacts/wordpress-adversarial-vulnerable"
93+
}
94+
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,8 @@
230230
"test:recipe-run-provenance": "tsx tests/recipe-run-provenance.test.ts",
231231
"test:fuzz-run-recipe": "tsx tests/fuzz-run-recipe.test.ts",
232232
"test:fuzz-suite-runner": "tsx tests/fuzz-suite-runner.test.ts",
233-
"test:adversarial-runtime": "tsx --test tests/transport-faults.test.ts tests/adversarial-campaign.test.ts tests/adversarial-browser.test.ts tests/browser-clock-control.test.ts tests/adversarial-recipe-orchestration.test.ts",
233+
"test:adversarial-runtime": "tsx --test tests/transport-faults.test.ts tests/adversarial-campaign.test.ts tests/adversarial-browser.test.ts tests/browser-clock-control.test.ts tests/adversarial-recipe-orchestration.test.ts tests/wordpress-adversarial-adapter.test.ts",
234+
"test:wordpress-adversarial-adapter": "tsx tests/wordpress-adversarial-adapter.test.ts",
234235
"test:playground-fuzz-suite-public": "tsx tests/playground-fuzz-suite-public.test.ts",
235236
"test:nested-fuzz-suite-recipe-command": "tsx tests/nested-fuzz-suite-recipe-command.test.ts",
236237
"test:wordpress-fuzz-suite-builders": "tsx tests/wordpress-fuzz-suite-builders.test.ts",

packages/runtime-playground/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export { preflightPhpWasmRuntimeAssets, PhpWasmRuntimeAssetIntegrityError, type
1717
export { browserPreviewAuthCookieUrls, browserPreviewNetworkPolicySummary, browserPreviewReadinessError, browserPreviewRouting, browserPreviewSecureContextError, browserPreviewTopology, browserPreviewOrigins, resolveBrowserPreviewUrl, type BrowserPreviewNetworkPolicy, type BrowserPreviewTopology } from "./browser-preview-routing.js"
1818
export { BROWSER_TRANSPORT_FAULT_CAPABILITIES, applyBrowserTransportFault, createBrowserTransportFaultAdapter, type BrowserTransportFaultAdapter } from "./browser-transport-faults.js"
1919
export { PLAYWRIGHT_CLOCK_CONTROL_CAPABILITIES, createBrowserClockController, type BrowserClockController } from "./browser-clock-control.js"
20+
export { WORDPRESS_ADVERSARIAL_ADAPTER_SCHEMA, WORDPRESS_ADVERSARIAL_CAPABILITIES, WORDPRESS_ADVERSARIAL_ORACLES, WORDPRESS_CLOCK_CONTROL_CAPABILITIES, WORDPRESS_HTTP_TRANSPORT_FAULT_CAPABILITIES, createWordPressAdversarialAdapter, evaluateWordPressAdversarialOracles, negotiateWordPressHttpTransportFaults, wordpressAdversarialActionSpec, wordpressHttpFaultConfigurationAction, wordpressNoveltySignals, wordpressSchedulerClockAction, type WordPressAdapterFidelity, type WordPressAdversarialAction, type WordPressAdversarialAdapter, type WordPressAdversarialCapability, type WordPressAdversarialSurface } from "./wordpress-adversarial-adapter.js"
2021
export { normalizePreviewReviewerAccess, previewReviewerAccess } from "./preview-reviewer-access.js"
2122
export { applyVfsMountSnapshots, materializePlaygroundMountsFromVfs, materializePlaygroundStagedInputs, type HostMountSnapshot, type MountMaterializationResult, type StagedInputMaterializationResult, type VfsMountSnapshot } from "./mount-materialization.js"
2223
export { buildReplayExportBlueprint, buildReplayableWordPressSiteBlueprint, buildReplayableWordPressSiteLimitations, writeReplayExportPackage, writeReplayableWordPressSiteBundle, type ReplayExportPackage, type ReplayExportPackageOptions, type ReplayableWordPressSiteBundle, type ReplayableWordPressSiteBundleManifest, type ReplayableWordPressSiteBundleOptions } from "./replayable-wordpress-site-bundle.js"

0 commit comments

Comments
 (0)