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.
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.