Skip to content

chore(template): Security Auditor DAST must clean up its own test artifacts - #29

Merged
HongmingWang-Rabbit merged 1 commit into
mainfrom
chore/security-dast-teardown
Apr 14, 2026
Merged

chore(template): Security Auditor DAST must clean up its own test artifacts#29
HongmingWang-Rabbit merged 1 commit into
mainfrom
chore/security-dast-teardown

Conversation

@HongmingWang-Rabbit

Copy link
Copy Markdown
Contributor

Summary

The Security Auditor's hourly DAST cron was creating test workspaces during security probes and never tearing them down, leaking rows into the live workspaces table. Adds an explicit 4a. DAST TEARDOWN step to the cron prompt with a track-and-cleanup pattern for workspaces, secrets, and plugins.

Root cause

Investigated under #17 (see comment 2026-04-14 02:14 UTC). The cron prompt already cleaned up secrets and plugins it created, but workspace-create probes had no teardown. Over four hourly runs we got four leaked rows with sequential IDs (aaaaaaaa-…/bbbbbbbb-…/cccccccc-…/dddddddd-…), each trapped in a restart loop against the missing config.yaml.

Platform log evidence at 2026-04-14 04:27:17 UTC (one sample, representative of every cycle):

POST  /admin/secrets                                           — create test secret
DELETE /admin/secrets/SEC-TEST-C5                              — cleanup (good)
POST  /workspaces/<PM>/plugins (404 for non-existent plugin)
DELETE /workspaces/<PM>/plugins/nonexistent-sec-test           — cleanup (good)
ProxyA2A: access denied <PM> → <BE>                            — CanCommunicate bypass probe
[workspace row `dddddddd-1234-5678-abcd-000000000004` appears in DB]
[no corresponding DELETE]

Changes

Single edit to org-templates/molecule-dev/org.yaml — adds step 4a to the Hourly security audit cron prompt:

  • Declare three tracking lists at the start of step 4: TESTS_WORKSPACES, TESTS_SECRETS, TESTS_PLUGINS.
  • Every probe that creates an artifact appends its ID to the right list.
  • At the end of step 4 (unconditional, runs even on partial audit failure), iterate each list and issue DELETE. Uses || true so a failed cleanup doesn't abort the audit.
  • Includes the prior-incident narrative inline so future editors understand why the step exists.

Why inline vs. platform-layer

A platform-level circuit-breaker (reject workspace creates from DAST, or TTL test workspaces) is still valid (proposed in #17 options B/C/D). But this template-layer fix is orthogonal and the cheapest immediate stop — it doesn't depend on any code change, and it closes the specific incident class we've already seen four times today.

Test plan

  • python -c "import yaml; yaml.safe_load(open('org-templates/molecule-dev/org.yaml'))" — YAML valid.
  • Next hourly cron run (after merge + template re-import): no new eeeeeeee-… row appears in workspaces.
  • docker ps | grep -c 'ws-[a-z]\{8\}-123' stays at 0 across successive runs.
  • Secret/plugin cleanup still works as before (no regression on existing teardown).

Related

🤖 Generated with Claude Code

…ifacts

Follow-up to root-cause analysis in #17 (see 2026-04-14 02:14 UTC comment).

The Security Auditor's hourly DAST was creating test workspaces, secrets,
and plugins to probe auth/validation logic — but only secrets and plugins
had teardown in the prompt. Workspace-create probes leaked rows into
`workspaces` with sequential IDs aaaaaaaa- bbbbbbbb- cccccccc- dddddddd-,
each trapped in a restart loop on missing config.yaml. Four hourly runs,
four leaked workspaces.

Adds explicit step 4a: DAST TEARDOWN. Maintains three lists (workspaces,
secrets, plugins) populated as probes run, and iterates them at the end
with DELETE calls. Uses `|| true` so partial teardown failures don't
break the audit, but every created artifact gets a cleanup attempt.

Doesn't remove the cleanup the cron was already doing for secrets/plugins
— just formalises the pattern so workspace-create (and any future probe
surface) is covered by the same contract.

Related:
- #17 — rogue workspace restart loop (root cause was this)
- #26 — audit cron routing (this PR sits alongside that structure)

@HongmingWang-Rabbit HongmingWang-Rabbit left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7-gate verification PASS:

  • Gate 1 CI: 6/6 green
  • Gate 2 Build: YAML-only, skipped
  • Gate 3 Tests: test_api.sh 62/62 pass
  • Gate 4 Security: cleanup scope bounded to self-created IDs; uses legacy /admin/secrets alias (valid); no unbounded deletion; no committed secrets
  • Gate 5 Design fit: Security Auditor role unchanged; PM-routing unaffected; uses standard REST endpoints
  • Gate 6 Line review: correct bash parameter expansion for ws:plugin split; || true guards against missing artifacts; quoting safe for UUIDs
  • Gate 7 Canvas: N/A

Merging.

@HongmingWang-Rabbit
HongmingWang-Rabbit merged commit 7b03cb8 into main Apr 14, 2026
6 checks passed
@HongmingWang-Rabbit
HongmingWang-Rabbit deleted the chore/security-dast-teardown branch April 14, 2026 05:20
HongmingWang-Rabbit pushed a commit that referenced this pull request Apr 15, 2026
Documents the 4-step hard-delete cascade implemented in
molecule-controlplane PR #29 (Stripe → Redis → Infra → DB rows),
how to read the org_purges audit table when a purge fails, the 30-day
GDPR deadline, and what the cascade deliberately does NOT cover
(WorkOS users, LLM provider history, Langfuse traces).

Cross-referenced from the "SaaS ops" block in CLAUDE.md so future
agents find it when handling erasure requests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
HongmingWang-Rabbit pushed a commit that referenced this pull request Apr 15, 2026
Extends the secret map with RESEND_API_KEY, RESEND_FROM_EMAIL,
STRIPE_API_KEY, STRIPE_WEBHOOK_SECRET — the four SaaS secrets the
control plane reads once the current PR stack (#29-#34 on
molecule-controlplane) ships.

Adds rotation procedures for each:
- Resend: low-blast-radius, best-effort sends, domain verification
  gotcha documented
- Stripe API key: independent rotation from webhook secret, live verify
  via /cp/billing/checkout
- Stripe webhook secret: 24h overlap window procedure using stripe
  trigger for live verify

Also adds Resend + Stripe entries to the emergency-contacts list.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
HongmingWang-Rabbit pushed a commit that referenced this pull request Apr 15, 2026
Tick 32 (manual) merged a large batch of PRs — the test counts in
CLAUDE.md were drifting behind reality by enough to matter:

- platform: 816 → 818 (YAML injection fix + sanitizeRuntime allowlist)
- canvas: 453 → 482 (12 CookieConsent + 17 PricingTable/billing)
- workspace-template: 1180 → 1179 (Hermes Phase 2a/2b dispatch tests
  landed but the test_hermes_providers env-var-leak fix removed a
  fragile flake-path count; net -1)

This is measured not guessed: running the full suites on fresh main.

Not in this sync but worth mentioning for the next retrospective:
- controlplane repo received the full GDPR/admin/usage/consent/email
  stack (#29-#34) — that work sits in molecule-controlplane, not
  monorepo CLAUDE.md
- monorepo picked up /pricing route, cookie consent banner, molecule-
  hitl plugin (#262), Hermes Phase 2a native Anthropic + 2b Gemini

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
molecule-ai Bot pushed a commit that referenced this pull request Apr 21, 2026
chore(template): Security Auditor DAST must clean up its own test artifacts
molecule-ai Bot pushed a commit that referenced this pull request Apr 21, 2026
Documents the 4-step hard-delete cascade implemented in
molecule-controlplane PR #29 (Stripe → Redis → Infra → DB rows),
how to read the org_purges audit table when a purge fails, the 30-day
GDPR deadline, and what the cascade deliberately does NOT cover
(WorkOS users, LLM provider history, Langfuse traces).

Cross-referenced from the "SaaS ops" block in CLAUDE.md so future
agents find it when handling erasure requests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
molecule-ai Bot pushed a commit that referenced this pull request Apr 21, 2026
Extends the secret map with RESEND_API_KEY, RESEND_FROM_EMAIL,
STRIPE_API_KEY, STRIPE_WEBHOOK_SECRET — the four SaaS secrets the
control plane reads once the current PR stack (#29-#34 on
molecule-controlplane) ships.

Adds rotation procedures for each:
- Resend: low-blast-radius, best-effort sends, domain verification
  gotcha documented
- Stripe API key: independent rotation from webhook secret, live verify
  via /cp/billing/checkout
- Stripe webhook secret: 24h overlap window procedure using stripe
  trigger for live verify

Also adds Resend + Stripe entries to the emergency-contacts list.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
molecule-ai Bot pushed a commit that referenced this pull request Apr 21, 2026
Tick 32 (manual) merged a large batch of PRs — the test counts in
CLAUDE.md were drifting behind reality by enough to matter:

- platform: 816 → 818 (YAML injection fix + sanitizeRuntime allowlist)
- canvas: 453 → 482 (12 CookieConsent + 17 PricingTable/billing)
- workspace-template: 1180 → 1179 (Hermes Phase 2a/2b dispatch tests
  landed but the test_hermes_providers env-var-leak fix removed a
  fragile flake-path count; net -1)

This is measured not guessed: running the full suites on fresh main.

Not in this sync but worth mentioning for the next retrospective:
- controlplane repo received the full GDPR/admin/usage/consent/email
  stack (#29-#34) — that work sits in molecule-controlplane, not
  monorepo CLAUDE.md
- monorepo picked up /pricing route, cookie consent banner, molecule-
  hitl plugin (#262), Hermes Phase 2a native Anthropic + 2b Gemini

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

1 participant