- JDK 21.
- Burp Suite Community or Professional compatible with Montoya API
2026.7.
All test traffic described here targets 127.0.0.1. The fixture refuses to bind to non-loopback interfaces.
From the repository root:
.\scripts\run-fixture.ps1Equivalent Gradle command:
.\gradlew.bat :fixture:run --args="--port=18080 --mode=vulnerable"Secure comparison mode:
.\scripts\run-fixture.ps1 -Mode secureThe fixture exposes:
| Method | Path | Purpose |
|---|---|---|
GET |
/health |
Mode and readiness |
POST |
/test/reset |
Deterministic state reset |
POST |
/api/invitations |
Create an invitation |
POST |
/api/invitations/{id}/accept |
Accept or replay an invitation |
DELETE |
/api/members/{id} |
Revoke membership |
GET |
/api/organizations/org-1/members |
State probe |
In vulnerable mode, replaying an invitation after revocation returns 409 while restoring membership. This deliberately models a response/state inconsistency.
In a second terminal:
.\scripts\run-burp-dev.ps1 `
-ProjectConfigFile .\lab\burp-project-options.jsonThe script:
- builds the extension;
- places the extension JAR on Burp's classpath;
- loads the public
Extensionclass through Burp's developer option; - uses
build/burp-dev-datainstead of the personal Burp data directory; - disables automatic updates for the isolated session.
- optionally imports an explicit Burp project configuration. The supplied lab
configuration creates a loopback listener on port
8080with interception disabled.
If Burp is installed elsewhere:
.\scripts\run-burp-dev.ps1 -BurpHome "C:\path\to\BurpSuiteCommunity"Find the active listener port under Burp proxy settings. Then run:
.\scripts\send-fixture-workflow-through-burp.ps1 -ProxyPort 8080The script sends:
RESET
→ CREATE invitation
→ ACCEPT invitation
→ REVOKE member
→ PROBE members
→ REPLAY acceptance
→ PROBE members
Expected vulnerable result:
Replay HTTP status: 409
Members before replay: 0
stateChanged: true
Members after replay: 1
Expected secure result:
Replay HTTP status: 409
Members before replay: 0
stateChanged: false
Members after replay: 0
- Open Burp Proxy history.
- Select the relevant fixture requests.
- Right-click and choose WorkflowGuard → Add to active workflow.
- Open the WorkflowGuard tab.
- Confirm the steps and their order.
- In Actors, keep the default captured-session actor or add identities with explicit initial
Cookie, maskedAuthorization, or both. Each identity receives an isolated cookie jar when a run starts. A custom actor without an authorization seed cannot inherit a captured bearer token. Explicit credentials are bound to one origin; create a separate actor definition if a workflow legitimately spans origins. - Assign an actor to each step. Keep lifecycle requests as
ACTION, assign the members request toPROBE, and optionally capture the member deletion a second time asCLEANUP. - Add
invitationIdin the Variables editor, sourced from the create response with JSON Pointer/id. Regex extractors use RE2/J syntax and intentionally reject backreferences and other constructs that cannot retain linear-time evaluation. To exercise stale-token handling, edit the variable and set a known expired or retired identifier under Stale object/token value. - Replace the invitation identifier in the accept request template with
${invitationId}, then save the template. - Add an
UNCHANGEDinvariant for/count, sourced from the members probe. Alternatively useEXPRESSIONwithafter:/count == before:/count. Configure request IDs or timestamps under Volatile JSON paths when a rootUNCHANGEDrule should ignore them. - Open Workflow graph. Confirm the high-confidence
${invitationId}data edge from creation to acceptance and the lifecycle edge from acceptance to revocation. Probe and cleanup steps occupy separate lanes. - Enable State-aware presets and Stale value, generate mutations, and select Replay accept after revocation, Use stale invitationId, or a case that swaps an action to another actor.
- Review the expanded actor-labelled sequence, request cap, delay, and scope restriction, then select Run selected case.
- Compare repeated executions under Result matrix. The assessment distinguishes the vulnerable state change, successful cleanup restoration, failed cleanup, execution failure, and runs without invariant checks. Only the latest run retains complete HTTP exchanges; previous rows are compacted to bounded metadata.
- Select the latest matrix row and use Export redacted JSON to save conservative evidence without HTTP headers or bodies, response reason phrases, cookie names, query values, identifiers, extracted values, authorization headers, tokens, passwords, API keys, or cookie values.
- With Publish invariant failures to Burp issues enabled, confirm that a failed post-mutation invariant is submitted as an informational WorkflowGuard audit issue. Burp Professional exposes it in All issues; Community accepts the API call but keeps that viewer behind the Pro license, so verify the submission message and inspect the same evidence in WorkflowGuard.
- Use Export to save a portable redacted workflow, then Import it as a separate workflow. Replace any
<redacted>session seed before replay.
In vulnerable mode, the post-mutation /count invariant fails and the post-cleanup comparison passes. HTTP 409 is retained as evidence: it does not by itself prove that application state remained unchanged.
Run every extension and fixture test:
.\gradlew.bat clean testThe fixture tests run secure and vulnerable lifecycle variants on ephemeral loopback ports. The root execution integration test additionally exercises the replay-after-revoke preset, dynamic invitation extraction, before/after invariant detection, cleanup, and post-cleanup verification against the vulnerable fixture.
The current Community Edition UI security regression and the preceding full execution campaign—including manual JAR loading, scope blocking, execution, cleanup, issue publication behavior, evidence export, unload/reload, and screenshots—are recorded in community-validation-20260731.md.