From cd09bd34868c420d5851f4ca48432b5ecfba5023 Mon Sep 17 00:00:00 2001 From: Kurt Seifried Date: Sat, 31 Oct 2026 12:23:40 -0600 Subject: [PATCH 1/2] docs: add REVIEW.md and Code Review Rules in AGENTS.md Add root REVIEW.md (with @AGENTS.md include and self-contained SecID-Service review checklist for Claude Code Review and GitHub Copilot Code Review) and add a matching `## Code Review Rules` section to AGENTS.md for OpenAI Codex (`codex review`). Closes #50 AI-Agent: Gemini --- AGENTS.md | 12 ++++++++++++ REVIEW.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 REVIEW.md diff --git a/AGENTS.md b/AGENTS.md index 0aeec34..6b1458e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -56,11 +56,23 @@ SecID-Service/ ├── e2e/ # Playwright against production; also the post-deploy gate ├── website/ # Astro static site (served from same Worker) ├── wrangler.toml # Cloudflare Worker config (account, KV, routes) +├── AGENTS.md, REVIEW.md # Agent instructions and code review rules (CLAUDE.md / GEMINI.md shims) └── .github/workflows/ ├── ci.yml # PR gate: typecheck, website build, unit tests (never deploys) └── registry-kv-upload.yml # Deploy: push to main, repository_dispatch from SecID, manual ``` +## Code Review Rules + +For automated and agent code reviews (`codex review`, Claude Code Review via [`REVIEW.md`](REVIEW.md), and GitHub Copilot Code Review), enforce these checks on every pull request: + +1. **Authority, Provenance, and CI/Deploy Guardrails:** Every merge to `main` runs `registry-kv-upload.yml`, syncs Cloudflare KV (`secid_REGISTRY`), and deploys the production Worker at `secid.cloudsecurityalliance.org`. Agent-authored commits must end with `AI-Agent: `, and agent PR bodies/comments must start with `*Agent: (SecID governing agent)*`. Changes to `.github/workflows/*.yml`, `wrangler.toml`, `AGENTS.md`, or `REVIEW.md` must never weaken unit tests (`npm test`), post-deploy Playwright verification (`npm run test:e2e`), or KV sync safety guards without an independent cross-vendor review recorded on the PR. +2. **MCP Output Sanitization and Untrusted Input Handling:** Every MCP tool (`resolve`, `lookup`, `describe` in `src/mcp.ts`) returning registry-derived fields must pass through `sanitizeResult()` / `sanitizeEnvelope()` (`src/sanitize.ts`), recursively relocating untrusted prose keys (`description`, `notes`, `note`, `purpose`, `reason`, `guidance`, `examples`, `disclaimer`, `summary`, `title`) into `_untrusted_prose` with `_prose_warning` and recursively capping strings/arrays/dicts via `capValue()` (`MAX_STRING_LEN`). `submit_feedback` (`src/feedback.ts`) must keep caller text inside the `untrusted` envelope (`schema_version: 2`). +3. **`/mcp` HTTP Method Gate Parity (ADR-015):** Changes to `src/method-gate.ts` must preserve explicit status codes (`405` + `Allow: POST, OPTIONS` for non-POST MCP requests, `302` to `/#mcp-setup` only for positively identified browsers, `501` for unregistered methods; never hold SSE streams open or redirect SDK clients), update both `test/method-gate.test.ts` and `e2e/mcp.spec.ts`, and stay in lockstep with `CSA-MCP-Core`. +4. **Abuse-Resistance and Operational Limits:** Preserve explicit input and payload guards (`secid` ≤ 1024 chars; MCP HTTP body ≤ 64 KiB counted on received bytes including chunked transfers; JSON-RPC batch ≤ 10 messages and no `submit_feedback` in batches; `submit_feedback` capped at 20 writes/min/isolate via `src/write-budget.ts`). +5. **KV Sync Safety and Resolver Semantics:** Preserve the 50-orphan deletion safety threshold in `scripts/upload-registry-kv.ts --sync` and the 25 MiB per-key KV value limit. Production code must read from KV (`src/kv-registry.ts`), never from the gitignored test snapshot `src/registry.ts`. Keep `src/type-registry.ts` (`TYPE_REGISTRY`) in lockstep with `CloudSecurityAlliance/SecID` and `SecID-Server-API`. +6. **Testing and Release-Train Guards:** Require Vitest coverage (`test/`) for parser, resolver, sanitization, KV, and MCP changes. When a registry source in `CloudSecurityAlliance/SecID` transitions to `version_required: true` or changes version lists, update `test/resolver.test.ts` here **first** before merging the `SecID` registry PR. Tag e2e tests (`e2e/`) that depend on external third-party URLs with `@third-party`. + ## Build, Test, and Development Commands Run from repo root: diff --git a/REVIEW.md b/REVIEW.md new file mode 100644 index 0000000..1629d5d --- /dev/null +++ b/REVIEW.md @@ -0,0 +1,50 @@ +@AGENTS.md + +# Code Review Rules — CloudSecurityAlliance/SecID-Service + +This file provides review instructions for automated and agent reviewers (Claude Code Review, GitHub Copilot Code Review, and human/agent reviewers). OpenAI Codex (`codex review` / `codex exec`) reads the matching [`## Code Review Rules`](AGENTS.md#code-review-rules) section in [`AGENTS.md`](AGENTS.md). + +Because **every merge to `main` runs `registry-kv-upload.yml`, syncs Cloudflare KV (`secid_REGISTRY`), and deploys the production Cloudflare Worker** at `secid.cloudsecurityalliance.org`, every pull request must be checked against the rules below. + +## 1. Authority, Provenance, and CI/Deploy Guardrails + +- **Agent attribution:** Agent-authored commits must end with the trailer `AI-Agent: `, and agent-authored PR descriptions and review comments must start with `*Agent: (SecID governing agent)*` (or the contributing agent's role). +- **No self-weakening of gates:** Any PR that modifies `.github/workflows/ci.yml`, `.github/workflows/registry-kv-upload.yml`, `wrangler.toml`, `AGENTS.md`, or `REVIEW.md` must be scrutinized to ensure it does not weaken pre-deploy unit tests (`npm test`), post-deploy Playwright verification (`npm run test:e2e`), or KV sync safety guards without an independent cross-vendor review recorded on the PR. +- **Secrets and configuration:** Never commit secrets or tokens. Verify that account IDs, KV namespace IDs (`secid_REGISTRY`, `secid_OBSERVABILITY`, `secid_FEEDBACK`), and Analytics Engine bindings (`secid_DEMAND`) in `wrangler.toml` are not accidentally altered or pointed at test resources. + +## 2. MCP Output Sanitization and Untrusted Input Handling + +- **Recursive MCP output sanitization (`src/sanitize.ts`):** Every MCP tool (`resolve`, `lookup`, `describe` in `src/mcp.ts`) that returns registry-derived data must pass its output through `sanitizeResult()` / `sanitizeEnvelope()`. Verify that changes to response shapes or `src/sanitize.ts`: + - Recursively traverse nested objects and arrays (`sanitizeDict`) so untrusted prose fields (`description`, `notes`, `note`, `purpose`, `reason`, `guidance`, `examples`, `disclaimer`, `summary`, `title`) at any depth are relocated into `_untrusted_prose` with the `_prose_warning` prompt-injection notice. + - Recursively cap strings, arrays, and nested dictionaries via `capValue()` (`MAX_STRING_LEN`) even on keys that are not relocated. +- **`submit_feedback` isolation (`src/feedback.ts`):** Caller-supplied text (`secid`, `message`, `suggested_urls`) must always remain inside the `untrusted` envelope (`schema_version: 2`) with the `handling` warning intact, and must never be interpolated into executable contexts or log strings unsanitized. + +## 3. `/mcp` HTTP Method Gate Parity (ADR-015) + +- **Explicit method handling (`src/method-gate.ts`):** The `/mcp` endpoint is stateless POST-only. Any change to `src/method-gate.ts` or `/mcp` routing in `src/index.ts` must preserve: + - `405 Method Not Allowed` (with `Allow: POST, OPTIONS`) for MCP-client `GET`/`HEAD` and other registered non-POST methods. + - `302` redirect to `/#mcp-setup` **only** for positively identified browsers (`Accept: text/html` + browser `User-Agent`). Never redirect an MCP SDK client or hold an SSE stream open (either causes SDK clients to reconnect in a tight loop). + - `501 Not Implemented` for unregistered HTTP methods. +- **Cross-repo parity:** `src/method-gate.ts` shares its contract with `CSA-MCP-Core`'s `src/mcp/method-gate.ts`. Any change requires updating both `test/method-gate.test.ts` and `e2e/mcp.spec.ts` and flagging the corresponding `CSA-MCP-Core` update. + +## 4. Abuse-Resistance and Operational Limits + +Verify that changes to `src/index.ts`, `src/api.ts`, `src/mcp.ts`, `src/feedback.ts`, or `src/write-budget.ts` do not bypass or weaken operational limits: +- **`secid` query length:** ≤ 1024 characters across both REST (`/api/v1/resolve`) and MCP (`resolve`, `lookup`, `describe`, `submit_feedback`). +- **MCP HTTP request body:** ≤ 64 KiB counted on actual received bytes (including chunked transfers without `Content-Length`), returning `413` when exceeded. +- **MCP JSON-RPC batching:** ≤ 10 messages per batch, and batches containing `submit_feedback` must be rejected with `400`. +- **`submit_feedback` rate and size limits:** `message` 1–4000 chars, `suggested_urls` ≤ 10 entries of ≤ 2048 chars each, and write budget capped at 20 writes per minute per isolate (`src/write-budget.ts`). + +## 5. KV Sync Safety and Resolver Semantics + +- **KV upload safety (`scripts/upload-registry-kv.ts`):** Preserve the 50-orphan deletion safety threshold in `--sync` mode (unless `--force` is explicitly passed) and the 25 MiB per-key KV value size check. +- **Stateless runtime (`src/kv-registry.ts`, `src/kv-resolve.ts`):** Production resolution reads exclusively from Cloudflare KV (`secid_REGISTRY`). `src/registry.ts` is a gitignored build snapshot used only to seed tests (`npm run build:registry`); runtime code must never import `src/registry.ts` directly. +- **Resolver fidelity (`src/parser.ts`, `src/resolver.ts`, `src/type-registry.ts`):** + - `src/type-registry.ts` (`TYPE_REGISTRY`) is the single source of truth for types and subtypes. If modified, flag the lockstep update required in `CloudSecurityAlliance/SecID` (`scripts/validate-type-list.py`, `scripts/validate-subtypes.py`). + - Preserve the four response outcomes (`found`, `corrected`, `related`, `not_found`), shortest-to-longest namespace matching, exact subpath preservation (no lossy normalization), and `SecID-Server-API` parity. + +## 6. Testing and Post-Deploy Verification + +- **Unit/integration coverage (`test/`):** Every parser, resolver, sanitization, KV, or MCP change must include Vitest regression tests (`npm test`). +- **Registry test guards during release trains:** When a registry source in `CloudSecurityAlliance/SecID` transitions from unversioned to `version_required: true` (or changes version lists), `test/resolver.test.ts` assertions in this repository must be updated or made version-agnostic **before** the `SecID` registry PR merges, so the triggered `Upload registry to KV` workflow does not fail in its pre-deploy test step. +- **Playwright E2E gate (`e2e/`):** User-facing website (`website/`) or `/mcp` routing changes must be covered in `e2e/`. Any e2e test that fetches external third-party URLs must be tagged `@third-party` so it is excluded from the production post-deploy gate (`--grep-invert @third-party`). From 0c1dbda058c16c7ab40c0e7afd12db30496b6331 Mon Sep 17 00:00:00 2001 From: Kurt Seifried Date: Sat, 31 Oct 2026 12:29:53 -0600 Subject: [PATCH 2/2] docs: address Codex review findings on REVIEW.md and Code Review Rules - Match src/sanitize.ts exact symbols: sanitizeResponseForMcp(), sanitizeDict(), sanitizeValue(), UNTRUSTED_TEXT_KEYS, registry_text_untrusted._warning, capString(), capValue(), MAX_FIELD_CHARS = 4000, MAX_ARRAY_ITEMS = 64 - Clarify OPTIONS CORS 204 pass-through vs 405 Method Not Allowed on registered non-POST, non-OPTIONS methods in src/method-gate.ts - Document exact isBrowserNavigation() predicate (rejects text/event-stream, Mcp-Protocol-Version, and Mcp-Session-Id first; accepts Sec-Fetch-Mode: navigate or Accept: text/html) - Require cross-vendor review and decision record unconditionally for all CI/deploy gate and reviewer-instruction changes Refs #50 AI-Agent: Gemini --- AGENTS.md | 8 ++++---- REVIEW.md | 44 +++++++++++++++++++++----------------------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 6b1458e..dddfbab 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -66,12 +66,12 @@ SecID-Service/ For automated and agent code reviews (`codex review`, Claude Code Review via [`REVIEW.md`](REVIEW.md), and GitHub Copilot Code Review), enforce these checks on every pull request: -1. **Authority, Provenance, and CI/Deploy Guardrails:** Every merge to `main` runs `registry-kv-upload.yml`, syncs Cloudflare KV (`secid_REGISTRY`), and deploys the production Worker at `secid.cloudsecurityalliance.org`. Agent-authored commits must end with `AI-Agent: `, and agent PR bodies/comments must start with `*Agent: (SecID governing agent)*`. Changes to `.github/workflows/*.yml`, `wrangler.toml`, `AGENTS.md`, or `REVIEW.md` must never weaken unit tests (`npm test`), post-deploy Playwright verification (`npm run test:e2e`), or KV sync safety guards without an independent cross-vendor review recorded on the PR. -2. **MCP Output Sanitization and Untrusted Input Handling:** Every MCP tool (`resolve`, `lookup`, `describe` in `src/mcp.ts`) returning registry-derived fields must pass through `sanitizeResult()` / `sanitizeEnvelope()` (`src/sanitize.ts`), recursively relocating untrusted prose keys (`description`, `notes`, `note`, `purpose`, `reason`, `guidance`, `examples`, `disclaimer`, `summary`, `title`) into `_untrusted_prose` with `_prose_warning` and recursively capping strings/arrays/dicts via `capValue()` (`MAX_STRING_LEN`). `submit_feedback` (`src/feedback.ts`) must keep caller text inside the `untrusted` envelope (`schema_version: 2`). -3. **`/mcp` HTTP Method Gate Parity (ADR-015):** Changes to `src/method-gate.ts` must preserve explicit status codes (`405` + `Allow: POST, OPTIONS` for non-POST MCP requests, `302` to `/#mcp-setup` only for positively identified browsers, `501` for unregistered methods; never hold SSE streams open or redirect SDK clients), update both `test/method-gate.test.ts` and `e2e/mcp.spec.ts`, and stay in lockstep with `CSA-MCP-Core`. +1. **Authority, Provenance, and CI/Deploy Guardrails:** Every merge to `main` runs `registry-kv-upload.yml`, syncs Cloudflare KV (`secid_REGISTRY`), and deploys the production Worker at `secid.cloudsecurityalliance.org`. Agent-authored commits must end with `AI-Agent: `, and agent PR bodies/comments must start with `*Agent: (SecID governing agent)*`. Every change to `.github/workflows/*.yml`, `wrangler.toml`, `AGENTS.md`, or `REVIEW.md` unconditionally requires an independent cross-vendor review and decision record, and must never weaken unit tests (`npm test`), post-deploy Playwright verification (`npm run test:e2e`), or KV sync safety guards; widening agent authority requires Kurt's approval first. +2. **MCP Output Sanitization and Untrusted Input Handling:** Every MCP tool (`resolve`, `lookup`, `describe` in `src/mcp.ts`) returning registry-derived fields must pass through `sanitizeResponseForMcp()` (`src/sanitize.ts`), recursively relocating `UNTRUSTED_TEXT_KEYS` (`description`, `notes`, `note`, `official_name`, `common_name`, `auth`, `contacts`, `contact`, `scope`, `policy`, `disclosure_policy`, `version_disambiguation`, `unversioned_behavior`, `parsing_instructions`) via `sanitizeDict()` / `sanitizeValue()` into `registry_text_untrusted` with `registry_text_untrusted._warning`, and stripping C0/C1 control, zero-width, and bidi characters while recursively capping strings/arrays/dicts via `capString()` / `capValue()` (`MAX_FIELD_CHARS = 4000`, `MAX_ARRAY_ITEMS = 64`). `submit_feedback` (`src/feedback.ts`) must keep caller text inside the `untrusted` envelope (`schema_version: 2`). +3. **`/mcp` HTTP Method Gate Parity (ADR-015):** `OPTIONS` passes through to CORS middleware (`204`). Changes to `src/method-gate.ts` must preserve explicit status codes (`405` + `Allow: POST, OPTIONS` for registered non-POST, non-OPTIONS methods `GET`, `HEAD`, `PUT`, `PATCH`, `DELETE`, `QUERY`, `TRACE`, `CONNECT` from non-browsers; `302` to `/#mcp-setup` only for `GET`/`HEAD` requests passing `isBrowserNavigation()`, which rejects `Accept: text/event-stream`, `Mcp-Protocol-Version`, and `Mcp-Session-Id` first and then accepts `Sec-Fetch-Mode: navigate` or `Accept: text/html`; `501` for unregistered methods; never hold SSE streams open or redirect SDK clients), update both `test/method-gate.test.ts` and `e2e/mcp.spec.ts`, and stay in lockstep with `CSA-MCP-Core`. 4. **Abuse-Resistance and Operational Limits:** Preserve explicit input and payload guards (`secid` ≤ 1024 chars; MCP HTTP body ≤ 64 KiB counted on received bytes including chunked transfers; JSON-RPC batch ≤ 10 messages and no `submit_feedback` in batches; `submit_feedback` capped at 20 writes/min/isolate via `src/write-budget.ts`). 5. **KV Sync Safety and Resolver Semantics:** Preserve the 50-orphan deletion safety threshold in `scripts/upload-registry-kv.ts --sync` and the 25 MiB per-key KV value limit. Production code must read from KV (`src/kv-registry.ts`), never from the gitignored test snapshot `src/registry.ts`. Keep `src/type-registry.ts` (`TYPE_REGISTRY`) in lockstep with `CloudSecurityAlliance/SecID` and `SecID-Server-API`. -6. **Testing and Release-Train Guards:** Require Vitest coverage (`test/`) for parser, resolver, sanitization, KV, and MCP changes. When a registry source in `CloudSecurityAlliance/SecID` transitions to `version_required: true` or changes version lists, update `test/resolver.test.ts` here **first** before merging the `SecID` registry PR. Tag e2e tests (`e2e/`) that depend on external third-party URLs with `@third-party`. +6. **Testing and Release-Train Guards:** Require Vitest coverage (`test/`) for parser, resolver, sanitization, KV, and MCP changes. When a registry source in `CloudSecurityAlliance/SecID` transitions to `version_required: true` or changes version lists, update `test/resolver.test.ts` / `test/website.spec.ts` here **first** before merging the `SecID` registry PR. Tag e2e tests (`e2e/`) that depend on external third-party URLs with `@third-party`. ## Build, Test, and Development Commands diff --git a/REVIEW.md b/REVIEW.md index 1629d5d..72bf632 100644 --- a/REVIEW.md +++ b/REVIEW.md @@ -2,49 +2,47 @@ # Code Review Rules — CloudSecurityAlliance/SecID-Service -This file provides review instructions for automated and agent reviewers (Claude Code Review, GitHub Copilot Code Review, and human/agent reviewers). OpenAI Codex (`codex review` / `codex exec`) reads the matching [`## Code Review Rules`](AGENTS.md#code-review-rules) section in [`AGENTS.md`](AGENTS.md). +Review instructions for Claude Code Review, GitHub Copilot Code Review, and human/agent reviewers. OpenAI Codex (`codex review` / `codex exec`) reads the matching [`## Code Review Rules`](AGENTS.md#code-review-rules) section in [`AGENTS.md`](AGENTS.md). -Because **every merge to `main` runs `registry-kv-upload.yml`, syncs Cloudflare KV (`secid_REGISTRY`), and deploys the production Cloudflare Worker** at `secid.cloudsecurityalliance.org`, every pull request must be checked against the rules below. +Because **every merge to `main` runs `registry-kv-upload.yml`, syncs Cloudflare KV (`secid_REGISTRY`), and deploys the production Cloudflare Worker** at `secid.cloudsecurityalliance.org`, check every PR against these rules: ## 1. Authority, Provenance, and CI/Deploy Guardrails -- **Agent attribution:** Agent-authored commits must end with the trailer `AI-Agent: `, and agent-authored PR descriptions and review comments must start with `*Agent: (SecID governing agent)*` (or the contributing agent's role). -- **No self-weakening of gates:** Any PR that modifies `.github/workflows/ci.yml`, `.github/workflows/registry-kv-upload.yml`, `wrangler.toml`, `AGENTS.md`, or `REVIEW.md` must be scrutinized to ensure it does not weaken pre-deploy unit tests (`npm test`), post-deploy Playwright verification (`npm run test:e2e`), or KV sync safety guards without an independent cross-vendor review recorded on the PR. -- **Secrets and configuration:** Never commit secrets or tokens. Verify that account IDs, KV namespace IDs (`secid_REGISTRY`, `secid_OBSERVABILITY`, `secid_FEEDBACK`), and Analytics Engine bindings (`secid_DEMAND`) in `wrangler.toml` are not accidentally altered or pointed at test resources. +- **Agent attribution:** Agent-authored commits must end with `AI-Agent: `, and agent PR descriptions/comments must start with `*Agent: (SecID governing agent)*` (or the contributing agent's role). +- **Governance and gate changes:** Every change to `.github/workflows/ci.yml`, `.github/workflows/registry-kv-upload.yml`, `wrangler.toml`, `AGENTS.md`, or `REVIEW.md` unconditionally requires an independent cross-vendor review and decision record, and must never weaken pre-deploy unit tests (`npm test`), post-deploy Playwright verification (`npm run test:e2e`), or KV sync safety guards. Widening agent authority requires Kurt's approval first. +- **Secrets and configuration:** Never commit secrets or tokens. Verify that account IDs, KV namespace IDs (`secid_REGISTRY`, `secid_OBSERVABILITY`, `secid_FEEDBACK`), and Analytics Engine bindings (`secid_DEMAND`) in `wrangler.toml` are not altered or pointed at test resources. ## 2. MCP Output Sanitization and Untrusted Input Handling -- **Recursive MCP output sanitization (`src/sanitize.ts`):** Every MCP tool (`resolve`, `lookup`, `describe` in `src/mcp.ts`) that returns registry-derived data must pass its output through `sanitizeResult()` / `sanitizeEnvelope()`. Verify that changes to response shapes or `src/sanitize.ts`: - - Recursively traverse nested objects and arrays (`sanitizeDict`) so untrusted prose fields (`description`, `notes`, `note`, `purpose`, `reason`, `guidance`, `examples`, `disclaimer`, `summary`, `title`) at any depth are relocated into `_untrusted_prose` with the `_prose_warning` prompt-injection notice. - - Recursively cap strings, arrays, and nested dictionaries via `capValue()` (`MAX_STRING_LEN`) even on keys that are not relocated. -- **`submit_feedback` isolation (`src/feedback.ts`):** Caller-supplied text (`secid`, `message`, `suggested_urls`) must always remain inside the `untrusted` envelope (`schema_version: 2`) with the `handling` warning intact, and must never be interpolated into executable contexts or log strings unsanitized. +- **Recursive MCP output sanitization (`src/sanitize.ts`):** Every MCP tool (`resolve`, `lookup`, `describe` in `src/mcp.ts`) returning registry-derived data must pass its output through `sanitizeResponseForMcp()`. Verify that changes to response shapes or `src/sanitize.ts`: + - Recursively traverse nested objects and arrays (`sanitizeDict()` / `sanitizeValue()`) so `UNTRUSTED_TEXT_KEYS` (`description`, `notes`, `note`, `official_name`, `common_name`, `auth`, `contacts`, `contact`, `scope`, `policy`, `disclosure_policy`, `version_disambiguation`, `unversioned_behavior`, `parsing_instructions`) at any depth are relocated into `registry_text_untrusted` with `registry_text_untrusted._warning`. + - Strip C0/C1 control, zero-width, and bidi-override characters (`CONTROL_RE`) and recursively cap strings, arrays, and nested dicts via `capString()` / `capValue()` (`MAX_FIELD_CHARS = 4000`, `MAX_ARRAY_ITEMS = 64`). +- **`submit_feedback` isolation (`src/feedback.ts`):** Caller-supplied text (`secid`, `message`, `suggested_urls`) must remain inside the `untrusted` envelope (`schema_version: 2`) with the `handling` warning intact. ## 3. `/mcp` HTTP Method Gate Parity (ADR-015) -- **Explicit method handling (`src/method-gate.ts`):** The `/mcp` endpoint is stateless POST-only. Any change to `src/method-gate.ts` or `/mcp` routing in `src/index.ts` must preserve: - - `405 Method Not Allowed` (with `Allow: POST, OPTIONS`) for MCP-client `GET`/`HEAD` and other registered non-POST methods. - - `302` redirect to `/#mcp-setup` **only** for positively identified browsers (`Accept: text/html` + browser `User-Agent`). Never redirect an MCP SDK client or hold an SSE stream open (either causes SDK clients to reconnect in a tight loop). +- **Explicit method handling (`src/method-gate.ts`):** The `/mcp` endpoint is stateless POST-only (`OPTIONS` passes through to CORS middleware and returns `204`). Any change to `src/method-gate.ts` or `/mcp` routing in `src/index.ts` must preserve: + - `405 Method Not Allowed` (with `Allow: POST, OPTIONS`) for registered non-POST, non-OPTIONS methods (`GET`, `HEAD`, `PUT`, `PATCH`, `DELETE`, `QUERY`, `TRACE`, `CONNECT`) from non-browsers. + - `302` redirect to `/#mcp-setup` **only** for `GET`/`HEAD` requests passing `isBrowserNavigation()` (which rejects MCP-client signals `Accept: text/event-stream`, `Mcp-Protocol-Version`, and `Mcp-Session-Id` first, then accepts `Sec-Fetch-Mode: navigate` **or** `Accept: text/html`). Never redirect an MCP SDK client or hold an SSE stream open. - `501 Not Implemented` for unregistered HTTP methods. - **Cross-repo parity:** `src/method-gate.ts` shares its contract with `CSA-MCP-Core`'s `src/mcp/method-gate.ts`. Any change requires updating both `test/method-gate.test.ts` and `e2e/mcp.spec.ts` and flagging the corresponding `CSA-MCP-Core` update. ## 4. Abuse-Resistance and Operational Limits -Verify that changes to `src/index.ts`, `src/api.ts`, `src/mcp.ts`, `src/feedback.ts`, or `src/write-budget.ts` do not bypass or weaken operational limits: -- **`secid` query length:** ≤ 1024 characters across both REST (`/api/v1/resolve`) and MCP (`resolve`, `lookup`, `describe`, `submit_feedback`). +Preserve explicit limits across `src/index.ts`, `src/api.ts`, `src/mcp.ts`, `src/feedback.ts`, and `src/write-budget.ts`: +- **`secid` query length:** ≤ 1024 characters across REST (`/api/v1/resolve`) and MCP (`resolve`, `lookup`, `describe`, `submit_feedback`). - **MCP HTTP request body:** ≤ 64 KiB counted on actual received bytes (including chunked transfers without `Content-Length`), returning `413` when exceeded. - **MCP JSON-RPC batching:** ≤ 10 messages per batch, and batches containing `submit_feedback` must be rejected with `400`. -- **`submit_feedback` rate and size limits:** `message` 1–4000 chars, `suggested_urls` ≤ 10 entries of ≤ 2048 chars each, and write budget capped at 20 writes per minute per isolate (`src/write-budget.ts`). +- **`submit_feedback` limits:** `message` 1–4000 chars, `suggested_urls` ≤ 10 entries of ≤ 2048 chars each, capped at 20 writes/min/isolate (`src/write-budget.ts`). ## 5. KV Sync Safety and Resolver Semantics -- **KV upload safety (`scripts/upload-registry-kv.ts`):** Preserve the 50-orphan deletion safety threshold in `--sync` mode (unless `--force` is explicitly passed) and the 25 MiB per-key KV value size check. -- **Stateless runtime (`src/kv-registry.ts`, `src/kv-resolve.ts`):** Production resolution reads exclusively from Cloudflare KV (`secid_REGISTRY`). `src/registry.ts` is a gitignored build snapshot used only to seed tests (`npm run build:registry`); runtime code must never import `src/registry.ts` directly. -- **Resolver fidelity (`src/parser.ts`, `src/resolver.ts`, `src/type-registry.ts`):** - - `src/type-registry.ts` (`TYPE_REGISTRY`) is the single source of truth for types and subtypes. If modified, flag the lockstep update required in `CloudSecurityAlliance/SecID` (`scripts/validate-type-list.py`, `scripts/validate-subtypes.py`). - - Preserve the four response outcomes (`found`, `corrected`, `related`, `not_found`), shortest-to-longest namespace matching, exact subpath preservation (no lossy normalization), and `SecID-Server-API` parity. +- **KV upload safety (`scripts/upload-registry-kv.ts`):** Preserve the 50-orphan deletion safety threshold in `--sync` mode (unless `--force` is passed) and the 25 MiB per-key KV value size check. +- **Stateless runtime (`src/kv-registry.ts`, `src/kv-resolve.ts`):** Production resolution reads exclusively from Cloudflare KV (`secid_REGISTRY`), never from the gitignored test snapshot `src/registry.ts`. +- **Resolver fidelity (`src/parser.ts`, `src/resolver.ts`, `src/type-registry.ts`):** Keep `src/type-registry.ts` (`TYPE_REGISTRY`) in lockstep with `CloudSecurityAlliance/SecID` (`scripts/validate-type-list.py`, `scripts/validate-subtypes.py`) and `SecID-Server-API`. ## 6. Testing and Post-Deploy Verification -- **Unit/integration coverage (`test/`):** Every parser, resolver, sanitization, KV, or MCP change must include Vitest regression tests (`npm test`). -- **Registry test guards during release trains:** When a registry source in `CloudSecurityAlliance/SecID` transitions from unversioned to `version_required: true` (or changes version lists), `test/resolver.test.ts` assertions in this repository must be updated or made version-agnostic **before** the `SecID` registry PR merges, so the triggered `Upload registry to KV` workflow does not fail in its pre-deploy test step. -- **Playwright E2E gate (`e2e/`):** User-facing website (`website/`) or `/mcp` routing changes must be covered in `e2e/`. Any e2e test that fetches external third-party URLs must be tagged `@third-party` so it is excluded from the production post-deploy gate (`--grep-invert @third-party`). +- **Unit/integration coverage (`test/`):** Require Vitest regression tests (`npm test`) for parser, resolver, sanitization, KV, or MCP changes. +- **Release-train test guards:** When a source in `CloudSecurityAlliance/SecID` transitions to `version_required: true` or changes version lists, update assertions in `test/resolver.test.ts` / `test/website.spec.ts` here **first** before merging the `SecID` registry PR. +- **Playwright E2E gate (`e2e/`):** Tag any e2e test that fetches external third-party URLs with `@third-party` so it is excluded from the production post-deploy gate (`--grep-invert @third-party`).