Found by a TLS deployment audit running real nginx in front of the app.
Every response carries Strict-Transport-Security: max-age=63072000; includeSubDomains; preload, unconditionally and with no configuration option. The README names HSTS twice and never states the value.
An operator deploying at hooks.example.com therefore silently commits every subdomain of example.com to HTTPS-only for two years, and preload invites permanent inclusion in browser preload lists. That is a decision with consequences well beyond this service, made on the operator's behalf without telling them.
Verified it is also emitted over plaintext direct to the app (curl http://127.0.0.1:19001/pages/login returns the header). Browsers ignore HSTS over plaintext, so that part is harmless — noting it only for completeness.
Nothing in the app broke under HSTS during testing; this is about the commitment being invisible, not about it malfunctioning.
Not milestoned: the header is correct and desirable for the intended deployment, and the risk is limited to operators who own a parent domain with non-HTTPS subdomains.
Definition of done:
The max-age, includeSubDomains and preload values are documented in README where HSTS is mentioned, stating plainly that includeSubDomains affects the whole parent domain and that preload is effectively irreversible on browser timescales.
HSTS is configurable — at minimum the ability to drop includeSubDomains and preload, since those are the two that reach beyond this service. Keep the current values as the default; this is about an escape hatch, not a weakening.
If made configurable, the iron rule applies: a set-but-unparseable value must abort startup, never silently fall back.
Found by a TLS deployment audit running real nginx in front of the app.
Every response carries `Strict-Transport-Security: max-age=63072000; includeSubDomains; preload`, unconditionally and with no configuration option. The README names HSTS twice and never states the value.
An operator deploying at `hooks.example.com` therefore silently commits **every subdomain of `example.com`** to HTTPS-only for two years, and `preload` invites permanent inclusion in browser preload lists. That is a decision with consequences well beyond this service, made on the operator's behalf without telling them.
Verified it is also emitted over plaintext direct to the app (`curl http://127.0.0.1:19001/pages/login` returns the header). Browsers ignore HSTS over plaintext, so that part is harmless — noting it only for completeness.
Nothing in the app broke under HSTS during testing; this is about the commitment being invisible, not about it malfunctioning.
Not milestoned: the header is correct and desirable for the intended deployment, and the risk is limited to operators who own a parent domain with non-HTTPS subdomains.
Definition of done:
- The `max-age`, `includeSubDomains` and `preload` values are documented in README where HSTS is mentioned, stating plainly that `includeSubDomains` affects the whole parent domain and that `preload` is effectively irreversible on browser timescales.
- HSTS is configurable — at minimum the ability to drop `includeSubDomains` and `preload`, since those are the two that reach beyond this service. Keep the current values as the default; this is about an escape hatch, not a weakening.
- If made configurable, the iron rule applies: a set-but-unparseable value must abort startup, never silently fall back.
Question for the owner. The README now documents the header and its value ("HSTS is always sent, and is not configurable"), so the documentation half of this is done. What remains is whether the value keeps preload.
Keep as is: max-age=63072000; includeSubDomains; preload. Any host name this runs under, and every subdomain of it, is HTTPS-only for two years, and browsers may bake that into their preload lists permanently.
Drop preload (recommended): max-age=63072000; includeSubDomains. Still meets REPO_POLICIES.md (one year minimum, includeSubDomains), and stops the app from inviting a permanent, browser-side commitment on the operator's behalf. Preloading is a decision for whoever owns the domain, made at hstspreload.org, not by a service that happens to run under it.
An opt-out variable is not proposed: the policy says the header is always sent.
If you want it dropped, reply here and it becomes a one-line change plus the README sentence; if you want it kept, this issue closes as documented.
Model: fable-5-1
Question for the owner. The README now documents the header and its value ("HSTS is always sent, and is not configurable"), so the documentation half of this is done. What remains is whether the value keeps `preload`.
- Keep as is: `max-age=63072000; includeSubDomains; preload`. Any host name this runs under, and every subdomain of it, is HTTPS-only for two years, and browsers may bake that into their preload lists permanently.
- Drop `preload` (recommended): `max-age=63072000; includeSubDomains`. Still meets `REPO_POLICIES.md` (one year minimum, `includeSubDomains`), and stops the app from inviting a permanent, browser-side commitment on the operator's behalf. Preloading is a decision for whoever owns the domain, made at hstspreload.org, not by a service that happens to run under it.
An opt-out variable is not proposed: the policy says the header is always sent.
If you want it dropped, reply here and it becomes a one-line change plus the README sentence; if you want it kept, this issue closes as documented.
Model: fable-5-1
sneak
was assigned by clawbot2026-09-21 09:20:32 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Found by a TLS deployment audit running real nginx in front of the app.
Every response carries
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload, unconditionally and with no configuration option. The README names HSTS twice and never states the value.An operator deploying at
hooks.example.comtherefore silently commits every subdomain ofexample.comto HTTPS-only for two years, andpreloadinvites permanent inclusion in browser preload lists. That is a decision with consequences well beyond this service, made on the operator's behalf without telling them.Verified it is also emitted over plaintext direct to the app (
curl http://127.0.0.1:19001/pages/loginreturns the header). Browsers ignore HSTS over plaintext, so that part is harmless — noting it only for completeness.Nothing in the app broke under HSTS during testing; this is about the commitment being invisible, not about it malfunctioning.
Not milestoned: the header is correct and desirable for the intended deployment, and the risk is limited to operators who own a parent domain with non-HTTPS subdomains.
Definition of done:
max-age,includeSubDomainsandpreloadvalues are documented in README where HSTS is mentioned, stating plainly thatincludeSubDomainsaffects the whole parent domain and thatpreloadis effectively irreversible on browser timescales.includeSubDomainsandpreload, since those are the two that reach beyond this service. Keep the current values as the default; this is about an escape hatch, not a weakening.Question for the owner. The README now documents the header and its value ("HSTS is always sent, and is not configurable"), so the documentation half of this is done. What remains is whether the value keeps
preload.max-age=63072000; includeSubDomains; preload. Any host name this runs under, and every subdomain of it, is HTTPS-only for two years, and browsers may bake that into their preload lists permanently.preload(recommended):max-age=63072000; includeSubDomains. Still meetsREPO_POLICIES.md(one year minimum,includeSubDomains), and stops the app from inviting a permanent, browser-side commitment on the operator's behalf. Preloading is a decision for whoever owns the domain, made at hstspreload.org, not by a service that happens to run under it.An opt-out variable is not proposed: the policy says the header is always sent.
If you want it dropped, reply here and it becomes a one-line change plus the README sentence; if you want it kept, this issue closes as documented.
Model: fable-5-1