Repository navigation
security(rail): local-only route sweep, public-creds structural guard, LTS security baseline and support window (rail 3.8.57) - #16209
Merged
Conversation
Filesystem sweep proving Hard Rules #15/#17 for the LTS: every route.ts under /api/mcp, /api/cli-tools/runtime, /api/services and the dashboard embed proxy is LOCAL_ONLY for every write method, the only read exemptions inside those roots are the pinned MCP audit routes, every one is classified MANAGEMENT (the class whose policy runs the loopback gate), the manage-scope carve-out cannot cover the spawning roots, and any src/app/api file (helpers included) that spawns directly belongs to a LOCAL_ONLY route. Validated by mutation: dropping the /api/services/ prefix, a new GET exemption, or a fork() helper module each turn it red, while check:route-guard-membership stays green on the helper case.
Structural layer for Hard Rule #11 next to the key-based check:public-creds gate: matches the credential formats themselves (Google OAuth client id, GOCSPX- secret, AIza key, GitHub OAuth app id, non-empty client_secret literal) anywhere in src/lib/oauth, open-sse and src/shared/constants, with resolvePublicCred() lines as the only sanctioned form, and pins that publicCreds.ts keeps its embedded defaults as masked byte arrays. Fixtures are built at runtime so the test file itself carries no scanner-matching literal. Validated by mutation: a bare const client id in src/shared/constants and an AIza key in an open-sse URL turn it red while the gate stays green.
SECURITY.md gains the LTS support window section for v3.9.x (stable/v3 receives fixes, security patches and provider updates; reporting goes through the same private advisory channel) with the window duration explicitly marked as a pending owner decision (T-GAP-3), and lists 3.9.x as the planned LTS line in Supported Versions. docs/security/LTS_SECURITY_BASELINE.md records the measured 2026-10-10 state with the command behind every number: npm audit, open code-scanning / Dependabot / secret-scanning alerts, the five security workflows, OpenSSF Scorecard, and the npm publish provenance path (OIDC trusted publishing + SLSA provenance on 3.8.49-3.8.51) plus the gaps left for the rehearsal.
Contributor
CI Coverage Report
Coverage artifact was not available for this run. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Rail release 3.8.57, task 15 (security + compliance for the 3.9.0 LTS). It adds automated proof for Hard Rules #15/#17 (spawn-capable routes are loopback-only) and #11 (no public upstream credential literals), records the measured scanner, alert and provenance state, and adds the LTS support window to
SECURITY.md. The window's duration is marked as a pending owner decision.No production code changes. Two new tests, one new doc page, and
SECURITY.md/docs/security/meta.jsonedits.What changed
tests/unit/route-guard-local-only-sweep.test.ts(Hard Rules feat(npm): add npm package publishing with CLI entry point #15/fix(ci): explicit .npmrc auth for npm publish #17) walks the filesystem and asserts:route.tsunder/api/mcp/,/api/cli-tools/runtime/and/api/services/isisLocalOnlyPath()with no method and for every write method. The same holds for the dashboard embed proxy/dashboard/providers/services/<name>/embed/**, which sits outsidesrc/app/apiand so outsidecheck:route-guard-membership;/api/mcp/auditand/api/mcp/audit/stats, and both routes export only GET and do not spawn;MANAGEMENT, the route class whose policy evaluatesisLocalOnlyPath();/api/mcp/only, and/api/services/and/api/cli-tools/runtime/stay inSPAWN_CAPABLE_PREFIXES;SPAWN_CAPABLE_ROUTE_ROOTSstill covers the three API roots;src/app/apithat spawns directly belongs to a LOCAL_ONLY route. "Any file" includes helper modules next toroute.ts. "Spawns" also coversfork(,execSync(,spawnSync(and a dynamicimport("child_process").tests/unit/public-creds-no-literals.test.ts(Hard Rule chore(ui): rebrand to OmniRoute #11) matches the credential values, whilecheck:public-credsmatches object keys. It looks for a Google OAuth client id, aGOCSPX-secret, anAIzakey, a GitHub OAuth app id and a non-emptyclient_secretliteral. It scanssrc/lib/oauth/,open-sse/andsrc/shared/constants/(the gate does not walk the last one). The only allowed form is a line that callsresolvePublicCred(). It also checks thatpublicCreds.tsstores its embedded defaults as masked byte arrays. A "gate blind spots" case runs the gate's ownfindLiteralCreds()on the same synthetic samples to show the gap. The fixtures are built at runtime, so the test file contains no string a secret scanner would match.SECURITY.md: adds 3.9.x as the planned LTS line in Supported Versions and a new## LTS support window (v3.9.x)section. It covers whatstable/v3receives, gives the duration as<T-GAP-3: owner decision pending — see ROADMAP.md>, and says LTS vulnerabilities go through the same private advisory channel.docs/security/LTS_SECURITY_BASELINE.md(registered indocs/security/meta.json) records the state measured on 2026-10-10, with the command behind each number:Nothing was published, dismissed or closed.
Validation
node --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test tests/unit/route-guard-local-only-sweep.test.ts tests/unit/public-creds-no-literals.test.ts tests/unit/check-route-guard-membership.test.ts tests/unit/check-public-creds.test.ts tests/unit/security-route-guard-tiers.test.ts tests/unit/publicCreds.test.ts/api/services/prefix/api/services/fork()next to a routecheck:route-guard-membershipstays greensrc/shared/constants/+AIzakey in anopen-sseURLcheck:public-credsstays greennpx eslint <new test files>npm run typecheck:corenpm run check:docs-allsecurity-docs-truthfulness-8097,docs-public-catalog-sensitive-pages,inference-auth-posture-13695npm run check:public-credsnpm run check:route-guard-membershipnpm pack --dry-run --json --ignore-scriptscheck:pack-artifactwas not run: no builtdist/, and a full build is out of scope