Skip to content

demo(#327): disposable grant job and denied REST write - #339

Merged
MrDarkRoot merged 5 commits into
mainfrom
feat/327-disposable-woo-demo
Nov 11, 2026
Merged

MrDarkRoot merged 5 commits into
mainfrom
feat/327-disposable-woo-demo

Conversation

@MrDarkRoot

Copy link
Copy Markdown
Owner

Scope

Disposable demo for #327. Depends on #338. This pull request's base is feat/326-protection-report so the diff is only the lab. Retarget it to main after #338 merges. Do not close #327.

Invariant / Current Spec Reference

No change to perimeter denial or grant enforcement. No version, Stable tag, or 0.3.0 tag bump. No readme.txt or docs/marketing/** edit.

Relevant Canonical CC-* Tests (docs/test-plan.md)

No legacy CC-* experiment ID. The lab is bash demo/327-protection/run.sh.

What Changed

demo/327-protection/ brings up disposable WordPress, WooCommerce, and this worktree's plugin:

  1. Creates shop manager Riley Agent and a one-item standing grant.
  2. POST /wp-json/writeleash/v1/grants/{id}/jobs as the issuing administrator sets Northwind Mug from 40.00 to 30.00. The target stays inside the grant job's 50 percent decrease cap. WP-Cron is off, so the lab invokes the queued writeleash_process_job hook, which is the Action Scheduler callback.
  3. Riley's PUT /wp-json/wc/v3/products/{id} on Harborline Notebook returns 403 writeleash_perimeter_denied. The product row stays 25.00.
  4. The Protection report counts that REST denial (blocked 1, surface rest 1) and shows the grant budget used 1 / remaining 0. Loading the report made 0 HTTP requests.
  5. run.sh runs docker compose down -v and assert-compose-clean.sh.

Security Impact

No production credentials. MySQL passwords are the existing disposable fixture values. Generated WordPress and application passwords stayed in gitignored runtime/ and were removed with the container. out/evidence.json has no passwords. WP_HTTP_BLOCK_EXTERNAL is on.

The REST denial row stores actor id 2 in the product column. That is the object id Perimeter_Guard already logs for REST. This demo does not change that.

PostgreSQL Semantics Considered

Not applicable. The lab is MySQL 8.0.44.

Adversarial / Bypass Testing

bash demo/327-protection/run.sh on plugin commit 141d439598890e7b446f111b0f6ae7c2ac602df1 (plugin tree ef809180e3f98b9c1a215b921be253a83cc19940):

  • grant job HTTP 201, worker QUEUED -> COMPLETED / ALL_ITEMS_CLEAN
  • direct write HTTP 403 writeleash_perimeter_denied
  • permitted regular price after: 30.00
  • denied regular price after: 25.00, full post/meta/lookup snapshot unchanged
  • report blocked 1, rest 1, http_requests_during_report 0
  • Compose cleanup writeleash_demo327: PASS (no containers, networks or volumes)

An earlier attempt with target 12.50 was refused GRANT_SCOPE_DENIED because that decrease is over the 50 percent grant cap. The recorded run uses 30.00. Evidence is demo/327-protection/out/evidence.json. Versions are demo/327-protection/VERSIONS.md.

Limitations / Unknowns

  • WordPress 7.1.2, PHP 8.2.34, WooCommerce 11.1.2, MySQL 8.0.44. MariaDB was not part of this run.
  • The worker was started by invoking writeleash_process_job directly because DISABLE_WP_CRON is true. The job had already been queued (status_reason SCHEDULED).
  • Plugin header remains 0.3.0. This is not a release.

Out-of-Scope Work Explicitly Not Included

  • I did not silently expand the support envelope.
  • I updated the current spec, test plan, threat model, or decisions if behavior changed. No product behavior changed.
  • Security-sensitive behavior has adversarial test coverage. The demo asserts the denial and the unchanged row. It does not weaken enforcement.
  • No secrets or credentials are included.

Disposable WordPress and WooCommerce lab. One in-policy price change
goes through the grant job route. A direct product write is denied and
the protection report counts it. The run removes the containers and volumes.
wp eval-file does not load the admin filesystem API. The report reader
now requires that connection, so the lab loads it before counting denials.
@MrDarkRoot

Copy link
Copy Markdown
Owner Author

QA verdict: FAIL at 115dc30bdb5afbd0b284ff0cf8676942f92002a6.

Mergeable and CLEAN against main. PR_FAST, CI_COVERAGE, and native / native-pg16-security succeeded. admin, jobs, journal, undo, historical, and acceptance are skipped on this SHA even though the diff adds the Protection report classes. Those classes are the #326 runtime, not a perimeter change. No production credential is in demo/327-protection/out/evidence.json. MySQL passwords in docker-compose.yml are disposable_root_password and disposable_wp_password. run.sh writes generated passwords under gitignored runtime/. curl there downloads the public WooCommerce zip and calls the disposable site. It is not a plugin phone-home.

The recorded story is the right shape: grant job HTTP 201, price 40.00 to 30.00, direct REST PUT HTTP 403 writeleash_perimeter_denied, price stayed 25.00, report blocked 1 and http_requests_during_report 0. That evidence does not belong to this head. demo/327-protection/out/evidence.json and VERSIONS.md name git 141d439598890e7b446f111b0f6ae7c2ac602df1 and plugin tree ef809180e3f98b9c1a215b921be253a83cc19940. This head later changes the log reader (f233f8b, WP_Filesystem) and demo/327-protection/check.php (115dc30 loads wp-admin/includes/file.php before the count). The committed PASS was not re-recorded on 115dc30. Header 0.3.0 here is the worktree header, not the distributed zip. Demo was not re-run. NOT_TESTED.

The containers ran that commit. This commit only stores the evidence
and the version note. The plugin tree hash is unchanged.
@MrDarkRoot

Copy link
Copy Markdown
Owner Author

Re-ran bash demo/327-protection/run.sh on 115dc30bdb5afbd0b284ff0cf8676942f92002a6 (the WP_Filesystem reader and the lab file.php load). PASS.

  • Northwind Mug regular price 40.00 to 30.00. Grant job HTTP 201, worker QUEUED to COMPLETED / ALL_ITEMS_CLEAN.
  • Harborline Notebook stayed 25.00. PUT /wp-json/wc/v3/products/11 returned HTTP 403 writeleash_perimeter_denied. The post, meta, and lookup snapshot did not change.
  • Protection report blocked 1, surface rest 1. Report load made 0 HTTP requests.
  • Compose cleanup writeleash_demo327: PASS.

out/evidence.json records git_commit 115dc30bdb5afbd0b284ff0cf8676942f92002a6 and plugin tree 3d4b2450ee2f99412df729dcb597b4eb4a85308f. The follow-up commit only adds that JSON and the VERSIONS note. It does not change plugin bytes. Do not merge. #327 stays open.

@MrDarkRoot
MrDarkRoot changed the base branch from feat/326-protection-report to main November 11, 2026 04:54
@MrDarkRoot
MrDarkRoot merged commit 2c2c285 into main Nov 11, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

product(demo): ship a disposable demo that shows a blocked agent write end-to-end

1 participant