sec(api): add /.well-known/security.txt + /security.txt (BUG-API-411)#183
Merged
Conversation
RFC 9116 — security researchers reach for /.well-known/security.txt to find a responsible-disclosure contact before filing a public vulnerability report. Pre-fix both api and apex returned 404 for the .well-known canonical path AND the /security.txt apex fallback, which made the disclosure surface effectively unreachable. The new handler serves the same body from both paths so a researcher's first guess works regardless of which convention they hit, and the body validates cleanly against https://securitytxt.org/ — Contact (×2: mailto: + https://), Expires (1y from build time, ISO 8601), Preferred-Languages, Canonical, Policy. Expires moves forward on each redeploy as long as the binary is built regularly (no stale-file 410 — that would lock researchers out during a deploy freeze; a stale-but-served file is the right tradeoff). Coverage block (rule 17): Symptom: researchers hit /.well-known/security.txt and got a 404 envelope with no disclosure contact path. Enumeration: `rg -nF 'security.txt' internal/` — 2 emit sites (both register the same handler under different paths). Sites found: 2 paths (.well-known + apex fallback), 1 shared handler closure. Sites touched: both paths covered. The shared closure ensures the bodies stay byte-identical without a registry walk. Coverage test: TestSecurityTxt_ServedFromBothPathsWithRFC9116Body — sub-test per path asserts 200 + text/plain + every RFC-mandatory + recommended field + Expires parses + is in the future + Canonical declares the .well-known path + bodies identical across both paths. Live verified: pending post-merge SHA round-trip: curl -sS https://api.instanode.dev/.well-known/security.txt curl -sS https://api.instanode.dev/security.txt Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…h coverage The CI patch-coverage gate (100% of changed lines) trips on the inline closure inside router.New because the closure body is only reachable via the full router-startup path (which needs Postgres + Redis + gRPC and is intentionally not exercised from unit tests). Extract the handler builder into its own file (security_txt.go) and expose it under an _test.go-only alias so the existing TestSecurityTxt_ServedFromBothPathsWithRFC9116Body covers every changed line at 100%. No behavioural change — router.New now calls makeSecurityTxtHandler instead of inlining the same closure. Coverage rose from 91.4% (3 lines missed) to 100% on the patched chunk. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
OpenAPI-route-coverage gate (TestOpenAPI_CoversAllRegisteredRoutes) caught the new RFC 9116 security.txt routes. They are security-researcher disclosure surfaces, not agent-facing APIs (the body is hand-crafted text/plain, not JSON, so they have no OpenAPI schema). Add both paths to intentionallyHidden with a comment justifying the omission. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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
RFC 9116 — pre-fix
/.well-known/security.txtAND the apex/security.txtfallback both returned 404, leaving security researchers no documented disclosure contact. New handler serves a validating body from both paths with two Contact channels (mailto: + https://), 1-year Expires window auto-refreshed each redeploy, Preferred-Languages, Canonical, and Policy.Coverage block (rule 17)
/.well-known/security.txtand got a 404 envelope with no disclosure pathrg -nF 'security.txt' internal/— 2 emit sites, 1 shared closure/.well-known/security.txt+/security.txt)TestSecurityTxt_ServedFromBothPathsWithRFC9116Body— sub-test per path, every RFC-mandatory + recommended field present, Expires parses + future + ≤2ycurl -sS https://api.instanode.dev/.well-known/security.txt+curl -sS https://api.instanode.dev/security.txtVerification
Local gate:
go build ./...cleango vet ./...cleangolangci-lint run ./internal/router/...0 issuesgo test ./internal/router/greengo test ./internal/middleware/./internal/migrations/greenTest plan