Repository navigation
Add Amber::Pipe::SecurityHeaders with a typed security_headers section - #1422
Draft
crimson-knight wants to merge 3 commits into
Draft
crimson-knight wants to merge 3 commits into
crimson-knight wants to merge 3 commits into
Conversation
Add a V2 pipe that sends X-Content-Type-Options, X-Frame-Options, a frame-ancestors Content-Security-Policy, Referrer-Policy, a conservative Permissions-Policy, Cross-Origin-Opener-Policy, and, on HTTPS requests, Strict-Transport-Security. It writes its headers before the rest of the pipeline and never replaces one already on the response, so an earlier pipe keeps its value and a controller has the final word. Values come from Amber::Configuration::SecurityHeadersConfig, a new security_headers section on AppConfig with secure defaults. Apps override it in config/environments/<env>.yml or with AMBER_SECURITY_HEADERS_* env variables; an empty value turns one header off. AppConfig#validate! rejects values containing a line break. HTTPS detection moves into HTTP::Request#https?, which the cookie jar's Secure flag now uses as well, so Strict-Transport-Security and secure cookies always agree. It keeps today's rule (HTTPS: on); the trusted-proxy work in #1415 defines the same method and replaces its body. Docs: routing guide pipelines and built-in pipe table, configuration guide section and env override table, migration guide note and checklist item.
…eaders - HTTP::Request#https? is also true when Amber terminates TLS itself (Settings#server_terminates_tls?, same name and body as PR #1415). - Pipeline applies the first plugged SecurityHeaders pipe to the 404 for a request that matches no route, which never reaches a pipeline's pipes. - The V2 loader validates security_headers after env overrides, so a value with a line break fails at boot instead of on every response. - Specs for native TLS, unrouted 404, Error-pipe 500, and load-time rejection from YAML and env; Ameba heredoc indent in the config spec. - Configuration guide: HSTS includeSubDomains consequence, headers.add, 404 coverage.
actions/checkout v4.4.0 and oprypin/install-crystal v1.9.4, each resolved from its tag to the commit SHA. Same lines as the pin on chore/v2-clear-compiler-warnings, so either branch merges cleanly first.
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.
What changed
Adds
Amber::Pipe::SecurityHeaders, a new V2 pipe. It is designed from scratch and does not use the 1.xAmber::Pipe::SecureHeaders.X-Content-Type-Options: nosniff,X-Frame-Options: DENY,Content-Security-Policy: frame-ancestors 'none',Referrer-Policy: strict-origin-when-cross-origin, a conservativePermissions-PolicyandCross-Origin-Opener-Policy: same-origin. On HTTPS requests only, it also sendsStrict-Transport-Security: max-age=63072000; includeSubDomains.call_next. A header already on the response is kept, so an earlier pipe keeps its value and a controller that assignsresponse.headers[...]has the final word. Error pages thatAmber::Pipe::Errorrenders for a failing controller carry the headers.Pipeline#callnow gives that 404 the headers of the first pluggedSecurityHeaderspipe. An app that plugs noSecurityHeaderspipe sees no change.Amber::Configuration::SecurityHeadersConfigatsecurity_headersonAppConfig, with aSettings#security_headersaccessor. Every value has a secure default. An app overrides a value inconfig/environments/<env>.ymlor withAMBER_SECURITY_HEADERS_*, and an empty value turns one header off. A value with a line break fails settings loading withConfigurationError, andAppConfig#validate!reports it too.HTTP::Request#https?now decides HTTPS in one place. A request counts as HTTPS when Amber terminates TLS itself (Settings#server_terminates_tls?, same name and body as Decide HTTPS and client IP through trusted proxies (server.trusted_proxies) #1415) or when it carriesHTTPS: on. The cookie jar'sSecureflag uses the same method, so HSTS and Secure cookies always agree.includeSubDomainsconsequence,headers.addnote, 404 coverage), the migration guide and CLAUDE.md..github/workflows/ci.ymlnow pinsactions/checkoutandoprypin/install-crystalto full commit SHAs. The SHAs were resolved from the v4.4.0 and v1.9.4 tags. The lines match chore(v2): clear the 25 compiler warnings so the build is GREEN #1421, so either branch merges cleanly first. The machine's dep-pin push gate requires the pin.Merge order with #1415
#1415 (
feature/trusted-proxy-https) adds the sameHTTP::Request#https?andSettings#server_terminates_tls?and changes the same line incookies/store.cr. Resolve each conflict in favor of #1415. Its trusted-proxy rule then replaces this branch's interim rule, which trustsHTTPS: onfrom any peer, and the pipe and cookie jar follow it with no further change.How it was verified
crystal-alpha build --no-codegen src/amber.cr: zero errors and zero warnings.crystal-alpha spec: 2437 examples, 0 failures, 0 errors, 0 pendingcrystal-alpha tool format --check src spec: clean.cdd58b3) on all 15 changed Crystal files: no finding that the v2-dev baseline does not already have.Amber::Validators::Params#validationthat v2-dev already prints. This branch adds none, and chore(v2): clear the 25 compiler warnings so the build is GREEN #1421 clears them.New specs cover:
HTTPS: on, on native TLS, off on plain HTTP and off when emptyCodex review (gpt-6-sol, xhigh) and resolutions
https?trusted only the caller-suppliedHTTPS: onheader. Native TLS requests missed HSTS and Secure cookies, and any plain HTTP client could claim HTTPS.https?is true whenSettings#server_terminates_tls?, with a spec. The trusted-peer boundary is #1415'sserver.trusted_proxiesrule, which replaces this method on merge, so it is not duplicated here. Until then theHTTPS: ontrust matches v2-dev's existing cookie-jar behavior. A spoofed header only gets HSTS sent back to the client that spoofed it.Pipeline#callapplies the first pluggedSecurityHeaderspipe'swrite_headersbefore rendering the 404. Specs cover the plugged and unplugged cases.validate!. A bad value would turn every response into a 500 instead of failing the boot.security_headersafter the env overrides. Specs cover a bad value from YAML and from env.headers.addwas undocumented.headers.add. Trusted-proxy specs belong to #1415.includeSubDomainscommits sibling subdomains to HTTPS without saying so.includeSubDomains. The secure default stays.The full Codex output is in the plan folder as
followup-37-unblocked-work/codex-amber-security-headers.md.Crystal conventions rubric
ai_docs:key is tracked by plan M-2b.AppConfigsection with the same pattern asStaticConfig/MailerConfig,EnvOverrideand aSettingsaccessornot_nil!/.as(T)EnvOverridelist_of_headers,sends_strict_transport_security?,write_headers,first_plugged_security_headerscrystal-alpha tool format --checkcleanThe MINOR rubric items have no open defects in the new code.
Plan milestone
This serves M-1 (Amber prerequisites), "security-headers pipe added", in
followup-34-mica-beryl-amber-v2-plan.md, section 7. It also supplies the headers that T7 (section 5) checks. Plugging the pipe into the generated browser and API pipelines is amber_cli template work and comes in a separate PR.