Skip to content

feat(scripts): extend §1.5 verb-canary coverage (Phase E)#215

Open
hyperpolymath wants to merge 1 commit into
mainfrom
phase-e-smoke-verb-canary-expansion
Open

feat(scripts): extend §1.5 verb-canary coverage (Phase E)#215
hyperpolymath wants to merge 1 commit into
mainfrom
phase-e-smoke-verb-canary-expansion

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Summary

Tightens scripts/hcg-policy-smoke.sh against three verb-governance regression classes the original three canaries (DELETE/PUT/PATCH on /cartridges and /health) don't catch. Single-lane HCG tier-2 channel (standards#91); Phase E (standards#100) is the active phase.

What this PR adds

Three new deny-mode verb-canary probes:

  1. OPTIONS /cartridgesglobal_verbs: [GET, POST] bans OPTIONS, but a CORS preflight auto-responder added later would silently bypass policy. The canary fails closed against that regression class.

  2. DELETE /cartridge/probe/invoke — exercises the regex route ^/cartridge/[A-Za-z0-9_.-]+/invoke$ under a banned verb. The existing exact-path canaries don't catch a regex-matcher regression where the path is accepted under any verb instead of only the verb the rule lists.

  3. GET /cartridges/ssg-mcp/webhook — the path is in the policy as a documented public exception, but only for POST. The canary verifies the {path, verb} pairing is enforced: GET on the same path must default-deny because no rule covers it.

Runbook §1.5 description updated to enumerate the expanded canary set; runbook version bump 0.4 → 0.5.

What this PR deliberately does NOT do

  • Probe HEAD. Curl with -X HEAD (vs --head) waits for a body the server will not send, which interacts badly with the script's --max-time 10. HEAD enforcement remains covered by the gateway's own unit tests; the §1.5 operator pre-check focuses on probes that survive curl's method quirks. The reasoning is captured inline in the script comment so a future maintainer doesn't add it back as an oversight.

  • Extend the --with-backend allow-path matrix. The authenticated routes the script probes in allow mode are the ones actually wired in BojRest.Router; the additional policy entries (graphql, sse, order, umoja/*, etc.) are declared-not-yet-wired per contract §8 and would 404 from BoJ, which the allow_or_upstream pattern misdiagnoses as gateway-deny. They stay in the deny matrix until they are wired in BoJ. The ssg-mcp-webhook-post route is not added to the --with-backend allow probes for the same reason.

  • Auto-derive the probe matrix from the policy YAML. The matrix stays hand-maintained and the parity-with-policy property remains a manual maintenance discipline. PR feat(scripts): hcg-policy-smoke.sh — §1.5 operator pre-check (Phase E) #210's commitment ("the script doubles as a policy-completeness checklist") is preserved.

  • Close standards#100. Per runbook §6.5 the joint-close happens after the §6.4 Trustfile flip, which itself follows the §3.3 100% production-soak window. Using Refs per the Phase E PR convention (chore(deps): bump nixpkgs from 01fbdee to 6368eda #38, feat(config): promote gateway policy example → live (Phase E §1.5) #208, feat(scripts): hcg-policy-smoke.sh — §1.5 operator pre-check (Phase E) #210).

Verification

  • bash -n scripts/hcg-policy-smoke.sh — syntax check passes.
  • Synthetic always-403 mock on :18443PASS=31 FAIL=0 (was 28); the three new canaries report PASS; exit 0.
  • --help and bad-args exit codes unchanged (64).
  • SPDX header MPL-2.0 unchanged.
  • Runbook cross-references resolve.

Channel position

standards#91 (parent, open)
├── #96 Phase A — closed
├── #97 Phase B — closed
├── #98 Phase C — closed
├── #99 Phase D — closed (joint-closed via boj-server#168)
└── #100 Phase E — IN PROGRESS
     ├── E5 runbook draft — boj-server#128 (landed)
     ├── E1 loopback prereqs — boj-server#130/#131/#132/#165/#173 (landed)
     ├── E1 deploy spec — http-capability-gateway#38 (landed)
     ├── E1 live policy promotion — boj-server#208 (landed)
     ├── §1.5 operator pre-check smoke — boj-server#210 (landed)
     ├── §1.5 verb-canary expansion — THIS PR (in review)
     ├── E1 .ctp signing — owner follow-up
     ├── E2 staging cut-over — owner follow-up
     ├── E3 telemetry verification — owner follow-up
     ├── E4 production rollout — owner follow-up
     └── §6.4 Trustfile flip + §6.5 joint-close — owner-only

Refs hyperpolymath/standards#91
Refs hyperpolymath/standards#100

🤖 Generated with Claude Code


Generated by Claude Code

Tightens `scripts/hcg-policy-smoke.sh` against three classes of
verb-governance regression the original three canaries (DELETE/PUT/
PATCH on `/cartridges` and `/health`) don't catch. Single-lane HCG
tier-2 channel (`standards#91`); Phase E (`standards#100`) is the
active phase.

Added probes:

1. **`OPTIONS /cartridges`** — `global_verbs: [GET, POST]` bans
   OPTIONS, but a CORS preflight auto-responder added later would
   silently bypass policy. The canary fails closed against that
   regression class.

2. **`DELETE /cartridge/probe/invoke`** — exercises the regex route
   `^/cartridge/[A-Za-z0-9_.-]+/invoke$` under a banned verb. The
   existing exact-path canaries don't catch a regex-matcher regression
   where the path is accepted under any verb instead of only the verb
   the rule lists.

3. **`GET /cartridges/ssg-mcp/webhook`** — the path is in the policy as
   a documented public exception, but only for POST. The canary
   verifies the `{path, verb}` pairing is enforced: GET on the same
   path must default-deny because no rule covers it.

Deliberate omission: HEAD. Curl with `-X HEAD` (vs `--head`) waits for
a body the server will not send, which interacts badly with the
script's `--max-time 10`. HEAD enforcement remains covered by the
gateway's own unit tests; the §1.5 operator pre-check focuses on
probes that survive curl's method quirks. The reasoning is captured
inline in the script comment so a future maintainer doesn't add it
back as an oversight.

Runbook §1.5 description updated to reflect the expanded canary set;
version bump 0.4 → 0.5; status line records the extension.

Verification:

- `bash -n scripts/hcg-policy-smoke.sh` — syntax check passes.
- Synthetic always-403 mock on :18443 — PASS=31 FAIL=0 (was 28),
  the three new canaries report PASS. Exits 0.
- `--help` and bad-args exit codes unchanged (64).

Out of scope:

- The `--with-backend` allow-path matrix is not extended here. The
  authenticated routes the script probes in allow mode are the ones
  actually wired in `BojRest.Router`; the additional policy entries
  (`graphql`, `sse`, `order`, `umoja/*`, etc.) are declared-not-yet-
  wired per contract §8 and would 404 from BoJ, which the
  `allow_or_upstream` pattern misdiagnoses as gateway-deny. They stay
  in the deny matrix until they are wired in BoJ.
- The `ssg-mcp-webhook-post` route is not added to the `--with-backend`
  allow probes for the same reason: the handler is in `openapi.yaml`
  but not yet in `router.ex`.
- The script does not parse `config/gateway-policy-boj.yaml` to derive
  the probe matrix; the matrix stays hand-maintained and the
  parity-with-policy property remains a manual maintenance discipline.
  PR #210's commitment ("the script doubles as a policy-completeness
  checklist") is preserved.

Channel position:

```
standards#91 (parent, open)
├── #96 Phase A — closed
├── #97 Phase B — closed
├── #98 Phase C — closed
├── #99 Phase D — closed (joint-closed via boj-server#168)
└── #100 Phase E — IN PROGRESS
     ├── E5 runbook draft — boj-server#128 (landed)
     ├── E1 loopback prereqs — boj-server#130/#131/#132/#165/#173 (landed)
     ├── E1 deploy spec — http-capability-gateway#38 (landed)
     ├── E1 live policy promotion — boj-server#208 (landed)
     ├── §1.5 operator pre-check smoke — boj-server#210 (landed)
     ├── §1.5 verb-canary expansion — THIS PR
     ├── E1 .ctp signing — owner follow-up
     ├── E2 staging cut-over — owner follow-up
     ├── E3 telemetry verification — owner follow-up
     ├── E4 production rollout — owner follow-up
     └── §6.4 Trustfile flip + §6.5 joint-close — owner-only
```

Refs hyperpolymath/standards#91
Refs hyperpolymath/standards#100

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 272 issues detected

Severity Count
🔴 Critical 15
🟠 High 138
🟡 Medium 119

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Stale AI session file -- delete",
    "type": "stale",
    "file": "GEMINI.md",
    "action": "delete",
    "rule_module": "root_hygiene",
    "severity": "medium"
  },
  {
    "reason": "Issue in abi-drift.yml",
    "type": "missing_timeout_minutes",
    "file": "abi-drift.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in codeql.yml",
    "type": "missing_timeout_minutes",
    "file": "codeql.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in container-publish.yml",
    "type": "missing_timeout_minutes",
    "file": "container-publish.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in dogfood-gate.yml",
    "type": "missing_timeout_minutes",
    "file": "dogfood-gate.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in dogfood-gate.yml",
    "type": "missing_timeout_minutes",
    "file": "dogfood-gate.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in dogfood-gate.yml",
    "type": "missing_timeout_minutes",
    "file": "dogfood-gate.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in dogfood-gate.yml",
    "type": "missing_timeout_minutes",
    "file": "dogfood-gate.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in dogfood-gate.yml",
    "type": "missing_timeout_minutes",
    "file": "dogfood-gate.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in dogfood-gate.yml",
    "type": "missing_timeout_minutes",
    "file": "dogfood-gate.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@hyperpolymath hyperpolymath marked this pull request as ready for review June 13, 2026 06:44
@hyperpolymath hyperpolymath enabled auto-merge (rebase) June 13, 2026 06:44
@hyperpolymath hyperpolymath disabled auto-merge June 13, 2026 06:44
@hyperpolymath hyperpolymath enabled auto-merge (rebase) June 13, 2026 06:44
@hyperpolymath hyperpolymath disabled auto-merge June 13, 2026 06:46
@hyperpolymath hyperpolymath enabled auto-merge (rebase) June 13, 2026 06:46
@hyperpolymath hyperpolymath disabled auto-merge June 13, 2026 06:51
@hyperpolymath hyperpolymath enabled auto-merge (rebase) June 13, 2026 06:52
@hyperpolymath hyperpolymath disabled auto-merge June 13, 2026 06:55
@hyperpolymath hyperpolymath enabled auto-merge (rebase) June 13, 2026 06:55
@hyperpolymath hyperpolymath disabled auto-merge June 13, 2026 07:32
@hyperpolymath hyperpolymath enabled auto-merge (squash) June 13, 2026 07:32
@hyperpolymath hyperpolymath disabled auto-merge June 13, 2026 09:07
@hyperpolymath hyperpolymath enabled auto-merge (rebase) June 13, 2026 09:07
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