Make the HSTS max-age configurable with HSTS_MAX_AGE (closes #271) #515

Merged
clawbot merged 1 commits from issue-271-hsts-max-age into next 2026-10-13 03:27:26 +02:00
Collaborator

Makes the Strict-Transport-Security max-age a setting, per sneak's ruling on #271.

  • HSTS_MAX_AGE is a Go duration. Unset, it is 87600h (ten years), sent as max-age=315360000; includeSubDomains; preload. The default was two years before.
  • 0 sends no Strict-Transport-Security header at all.
  • A negative value, or one that is not a whole number of seconds (1.5s), stops startup with an error naming the variable, as an unparseable value does. A fraction is refused, not rounded.
  • includeSubDomains and preload stay fixed; the ruling covers only the duration.
  • The value is shown on /settings and in the startup log line.
  • README: the HSTS section is rewritten for the setting, and the variable is added to the configuration table and to the list of values that abort startup.

Not visible in the diff:

  • A Config built in code, as tests do, has a zero HSTSMaxAge and so sends no header. Only config.New applies the default.
  • Disclosure: nothing above zero is refused, so a value under one year is accepted although REPO_POLICIES.md asks for at least one year. The ruling leaves the duration to the operator.
  • Reading taken, from the plan comment: "disable" means no header, not max-age=0. For browsers that already saw the header, the README says how to end the policy early: a short HSTS_MAX_AGE such as 1s, or max-age=0 sent by a reverse proxy.
  • The README's includeSubDomains text differs from the issue's premise: the header covers subdomains of the hostname that sends it, not of its parent domain.

Model: opus-5-5

Makes the `Strict-Transport-Security` max-age a setting, per sneak's ruling on https://git.eeqj.de/sneak/webhooker/issues/271. - `HSTS_MAX_AGE` is a Go duration. Unset, it is `87600h` (ten years), sent as `max-age=315360000; includeSubDomains; preload`. The default was two years before. - `0` sends no `Strict-Transport-Security` header at all. - A negative value, or one that is not a whole number of seconds (`1.5s`), stops startup with an error naming the variable, as an unparseable value does. A fraction is refused, not rounded. - `includeSubDomains` and `preload` stay fixed; the ruling covers only the duration. - The value is shown on `/settings` and in the startup log line. - README: the HSTS section is rewritten for the setting, and the variable is added to the configuration table and to the list of values that abort startup. Not visible in the diff: - A `Config` built in code, as tests do, has a zero `HSTSMaxAge` and so sends no header. Only `config.New` applies the default. - Disclosure: nothing above zero is refused, so a value under one year is accepted although `REPO_POLICIES.md` asks for at least one year. The ruling leaves the duration to the operator. - Reading taken, from the plan comment: "disable" means no header, not `max-age=0`. For browsers that already saw the header, the README says how to end the policy early: a short `HSTS_MAX_AGE` such as `1s`, or `max-age=0` sent by a reverse proxy. - The README's `includeSubDomains` text differs from the issue's premise: the header covers subdomains of the hostname that sends it, not of its parent domain. Model: opus-5-5
clawbot added the needs-review label 2026-10-13 01:12:12 +02:00
clawbot self-assigned this 2026-10-13 01:12:12 +02:00
Author
Collaborator

Review failed: one finding.

  • README.md line 898, section "HSTS": "A browser that sees the header refuses plaintext HTTP to that hostname until max-age runs out" is not true as written. Under RFC 6797 section 8.1 a browser ignores the header when it arrives over plain HTTP, and webhooker sends it on its plain-HTTP listener as well, so the sentence tells anyone who has opened webhooker at http://localhost:8080 that their browser now refuses plain HTTP there for ten years. Acceptable: the sentence says the header must arrive over HTTPS, for example "A browser that receives the header over HTTPS refuses plaintext HTTP to that hostname …".

Model: opus-5-5

Review failed: one finding. - `README.md` line 898, section "HSTS": "A browser that sees the header refuses plaintext HTTP to that hostname until `max-age` runs out" is not true as written. Under RFC 6797 section 8.1 a browser ignores the header when it arrives over plain HTTP, and webhooker sends it on its plain-HTTP listener as well, so the sentence tells anyone who has opened webhooker at `http://localhost:8080` that their browser now refuses plain HTTP there for ten years. Acceptable: the sentence says the header must arrive over HTTPS, for example "A browser that receives the header over HTTPS refuses plaintext HTTP to that hostname …". Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-13 02:11:42 +02:00
clawbot force-pushed issue-271-hsts-max-age from c90afd9e35 to 9b39b865ca 2026-10-13 02:32:51 +02:00 Compare
Author
Collaborator
  • README.md HSTS section: the sentence now says the header must arrive over HTTPS, and adds that browsers ignore it over plain HTTP. The paragraph on HSTS_MAX_AGE=0 had the same mistake and now also says "received it over HTTPS".

Model: opus-5-5

- `README.md` HSTS section: the sentence now says the header must arrive over HTTPS, and adds that browsers ignore it over plain HTTP. The paragraph on `HSTS_MAX_AGE=0` had the same mistake and now also says "received it over HTTPS". Model: opus-5-5
clawbot added needs-review and removed needs-rework labels 2026-10-13 02:33:01 +02:00
Author
Collaborator

Review failed: one finding.

  • README.md lines 916-918, section "HSTS": "Only a max-age=0 header received over HTTPS clears it sooner. webhooker never sends one; a reverse proxy in front of it can." is not true. Under RFC 6797 section 8.1, every later header a browser receives over HTTPS replaces the max-age it stored. That means webhooker can end the policy early by itself: with HSTS_MAX_AGE=1s, a returning browser drops it one second after its next visit. As written, the README sends an operator who wants to back out to a reverse proxy, when 0 is the one setting that leaves earlier visitors held for the full max-age they saw. Acceptable: the paragraph says that a later header received over HTTPS replaces the stored max-age. Two things then end the policy sooner: a short HSTS_MAX_AGE such as 1s, kept until returning browsers have picked it up, or a max-age=0 header from a reverse proxy.

Model: opus-5-5

Review failed: one finding. - `README.md` lines 916-918, section "HSTS": "Only a `max-age=0` header received over HTTPS clears it sooner. webhooker never sends one; a reverse proxy in front of it can." is not true. Under RFC 6797 section 8.1, every later header a browser receives over HTTPS replaces the `max-age` it stored. That means webhooker can end the policy early by itself: with `HSTS_MAX_AGE=1s`, a returning browser drops it one second after its next visit. As written, the README sends an operator who wants to back out to a reverse proxy, when `0` is the one setting that leaves earlier visitors held for the full `max-age` they saw. Acceptable: the paragraph says that a later header received over HTTPS replaces the stored `max-age`. Two things then end the policy sooner: a short `HSTS_MAX_AGE` such as `1s`, kept until returning browsers have picked it up, or a `max-age=0` header from a reverse proxy. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-13 02:54:14 +02:00
clawbot added 1 commit 2026-10-13 03:10:18 +02:00
HSTS_MAX_AGE, a Go duration, sets the max-age of the
Strict-Transport-Security header. Unset, it is ten years (87600h);
0 sends no header at all. A negative value, or one that is not a
whole number of seconds, stops startup like an unparseable one.
includeSubDomains and preload stay in the header. The value is shown
on /settings and in the startup log, and the README's HSTS section
now says what each part of the header commits the operator to.

Model: opus-5-5
clawbot force-pushed issue-271-hsts-max-age from 9b39b865ca to 7c9b241704 2026-10-13 03:10:18 +02:00 Compare
Author
Collaborator
  • README.md section "HSTS": the last paragraph now says each header received over HTTPS replaces the stored max-age, and names both ways to end the policy sooner: a short HSTS_MAX_AGE such as 1s kept until returning browsers have picked it up, or max-age=0 from a reverse proxy. No other sentence in the section had the same error. The PR body line about this is updated to match.

Model: opus-5-5

- `README.md` section "HSTS": the last paragraph now says each header received over HTTPS replaces the stored `max-age`, and names both ways to end the policy sooner: a short `HSTS_MAX_AGE` such as `1s` kept until returning browsers have picked it up, or `max-age=0` from a reverse proxy. No other sentence in the section had the same error. The PR body line about this is updated to match. Model: opus-5-5
clawbot added needs-review and removed needs-rework labels 2026-10-13 03:10:36 +02:00
Author
Collaborator

Review passed.

Model: opus-5-5

Review passed. Model: opus-5-5
clawbot merged commit 957de7f99f into next 2026-10-13 03:27:26 +02:00
clawbot deleted branch issue-271-hsts-max-age 2026-10-13 03:27:27 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/webhooker#515