fix(release): de webbundel raakte .htaccess en security.txt kwijt (#1888) #1903

Merged
brenno merged 1 commit from fix/webbundel-dotfile-sweep into main 2026-08-31 22:48:47 +00:00
Owner

Wat er stuk was

De dotfile-sweep uit #1888 haalt .DS_Store uit build/web en hield als "bewust meegenomen" alleen aan wat releaseArtefacten noemt. Die lijst beschrijft wat de inpakstap er zelf bij legt — niet wat de bundel bevat. De twee dotfiles die er wél horen komen ergens anders vandaan: flutter build web kopieert ze uit web/.

Weg waren dus build/web/.htaccess (de header-vorm-harding van #849) en build/web/.well-known/security.txt (het meldadres uit RFC 9116).

De release van v0.5.0 viel hierop om in fase 1: build/web/.htaccess is missing. Het meldadres was nergens gedekt en verdween stil. De live site is niet geraakt — de release brak af vóór fase 3, en ocideck.librekat.nl/.well-known/security.txt geeft nog 200.

Waarom het langs de poorten kwam

  1. Een denylist werd een allowlist, maar het domein werd niet opgeschreven — de goede lijst werd de lijst die toevallig binnen handbereik lag.
  2. De bron was gegate (security_txt_test, web_htaccess_headers_test), het artefact niet. Alleen .htaccess had ook een controle op de gebouwde bundel, dus er ging één lampje branden in plaats van twee.
  3. make check-web draait niet per PR (alleen check-full lokaal en op de tag). De per-PR-test die #1888 wél toevoegde, draaide op een met de hand gebouwde bundel die precies de bestanden miste die de wijziging brak.

Wat er nu staat

Door constructie. bewusteDotfiles leest web/ in plaats van een lijst — een dotfile die daar bij komt overleeft vanzelf. nietUitleveren blijft de enige plek waar "bewust niet uitleveren" staat en wint van die herkomst (web/.DS_Store bestaat op een Mac ook).

Een afgeleide poort in plaats van een vastgepinde. Er is niet security.txt naast .htaccess gezet — dan komt dezelfde fout één bestand later terug. check_web_hardening.dart loopt nu web/ langs en eist dat élke ingang de bundel haalt, en importeert nietUitleveren in plaats van hem te herhalen.

Naar links geschoven. pack_web_release_test.dart spiegelt de échte web/-boom in een nagebouwde bundel. Dat zet de invariant op de per-PR-poort.

Toetsing

  • Regressietest eerst rood tegen de onherstelde code (de beveiligingsheaders horen met de bundel mee (#849)), daarna groen.
  • Mutatietest op de nieuwe per-PR-test: met het gedrag van #1888 teruggezet wordt hij rood (web/.well-known/security.txt hoort de bundel te halen), na herstel groen. #1888 zou dus niet groen zijn gemerged.
  • Sabotage-en-herstel op de afgeleide poort: rood op een verdwenen .well-known/security.txt én op een verdwenen favicon.png, groen na herstel.
  • make check-full groen (inclusief check-secrets, sast, check-web).

Bewaker

Dit raakt een publieke belofte: het meldadres uit RFC 9116 werd niet uitgeleverd. De richting is herstellend — de belofte wordt weer waargemaakt en docs/CHECKS.md beschrijft voortaan wat er werkelijk bewaakt wordt. Er is geen afweging gemaakt waarbij een waarde het aflegde. Formaat, opslag, afhankelijkheden en uitgaand verkeer blijven ongemoeid.

Wat hierna nog open staat

Een wijziging aan tool/pack_web_release.dart ziet per PR nog steeds geen échte flutter build web. Voorstel voor een vervolg-PR: een padgefilterde web-gate-workflow naar het model van linux-build.yml, op pull_request + push: main + workflow_dispatch, met een test die dat padfilter bewaakt (zoals native_build_triggers_test.dart dat voor de platformbuilds doet).

Co-Authored-By: Claude Opus 5 noreply@anthropic.com

## Wat er stuk was De dotfile-sweep uit #1888 haalt `.DS_Store` uit `build/web` en hield als "bewust meegenomen" alleen aan wat `releaseArtefacten` noemt. Die lijst beschrijft wat de inpakstap er **zelf bij legt** — niet wat de bundel bevat. De twee dotfiles die er wél horen komen ergens anders vandaan: `flutter build web` kopieert ze uit `web/`. Weg waren dus `build/web/.htaccess` (de header-vorm-harding van #849) en `build/web/.well-known/security.txt` (het meldadres uit RFC 9116). De release van v0.5.0 viel hierop om in fase 1: `build/web/.htaccess is missing`. Het meldadres was nergens gedekt en verdween stil. **De live site is niet geraakt** — de release brak af vóór fase 3, en `ocideck.librekat.nl/.well-known/security.txt` geeft nog 200. ## Waarom het langs de poorten kwam 1. Een denylist werd een allowlist, maar het domein werd niet opgeschreven — de goede lijst werd de lijst die toevallig binnen handbereik lag. 2. De **bron** was gegate (`security_txt_test`, `web_htaccess_headers_test`), het **artefact** niet. Alleen `.htaccess` had ook een controle op de gebouwde bundel, dus er ging één lampje branden in plaats van twee. 3. `make check-web` draait niet per PR (alleen `check-full` lokaal en op de tag). De per-PR-test die #1888 wél toevoegde, draaide op een met de hand gebouwde bundel die precies de bestanden miste die de wijziging brak. ## Wat er nu staat **Door constructie.** `bewusteDotfiles` leest `web/` in plaats van een lijst — een dotfile die daar bij komt overleeft vanzelf. `nietUitleveren` blijft de enige plek waar "bewust niet uitleveren" staat en wint van die herkomst (`web/.DS_Store` bestaat op een Mac ook). **Een afgeleide poort in plaats van een vastgepinde.** Er is niet `security.txt` naast `.htaccess` gezet — dan komt dezelfde fout één bestand later terug. `check_web_hardening.dart` loopt nu `web/` langs en eist dat élke ingang de bundel haalt, en importeert `nietUitleveren` in plaats van hem te herhalen. **Naar links geschoven.** `pack_web_release_test.dart` spiegelt de échte `web/`-boom in een nagebouwde bundel. Dat zet de invariant op de per-PR-poort. ## Toetsing - Regressietest eerst rood tegen de onherstelde code (`de beveiligingsheaders horen met de bundel mee (#849)`), daarna groen. - Mutatietest op de nieuwe per-PR-test: met het gedrag van #1888 teruggezet wordt hij rood (`web/.well-known/security.txt hoort de bundel te halen`), na herstel groen. #1888 zou dus niet groen zijn gemerged. - Sabotage-en-herstel op de afgeleide poort: rood op een verdwenen `.well-known/security.txt` én op een verdwenen `favicon.png`, groen na herstel. - `make check-full` groen (inclusief `check-secrets`, `sast`, `check-web`). ## Bewaker Dit raakt een **publieke belofte**: het meldadres uit RFC 9116 werd niet uitgeleverd. De richting is herstellend — de belofte wordt weer waargemaakt en `docs/CHECKS.md` beschrijft voortaan wat er werkelijk bewaakt wordt. Er is geen afweging gemaakt waarbij een waarde het aflegde. Formaat, opslag, afhankelijkheden en uitgaand verkeer blijven ongemoeid. ## Wat hierna nog open staat Een wijziging aan `tool/pack_web_release.dart` ziet per PR nog steeds geen échte `flutter build web`. Voorstel voor een vervolg-PR: een padgefilterde `web-gate`-workflow naar het model van `linux-build.yml`, op `pull_request` + `push: main` + `workflow_dispatch`, met een test die dat padfilter bewaakt (zoals `native_build_triggers_test.dart` dat voor de platformbuilds doet). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fix(release): de webbundel raakte .htaccess en security.txt kwijt (#1888)
All checks were successful
scans / scans (pull_request) Successful in 2m35s
static-gate / static-gate (pull_request) Successful in 6m6s
51560d7930
De dotfile-sweep uit #1888 haalt `.DS_Store` uit `build/web` en hield als
"bewust meegenomen" alleen aan wat `releaseArtefacten` noemt. Die lijst
beschrijft wat de inpakstap er zelf bij legt — niet wat de bundel bevat.
De twee dotfiles die er wél horen komen ergens anders vandaan:
`flutter build web` kopieert ze uit `web/`. Weg waren dus de
beveiligingsheaders van #849 en het meldadres uit RFC 9116.

De release van v0.5.0 viel hierop om: `build/web/.htaccess is missing`.
Het meldadres was nergens gedekt en verdween stil — de live site is niet
geraakt, want de release brak af vóór het uitrollen.

Drie lagen, van sterk naar zwak:

- Door constructie: `bewusteDotfiles` leest `web/` in plaats van een
  lijst, dus een dotfile die daar bij komt overleeft vanzelf.
  `nietUitleveren` wint van die herkomst — `web/.DS_Store` bestaat op een
  Mac ook.
- Afgeleide poort: `check_web_hardening.dart` pinde `.htaccess` bij naam.
  Er is niet `security.txt` naast gezet — dan komt dezelfde fout één
  bestand later terug. Het loopt nu `web/` langs en eist dat élke ingang
  de bundel haalt, en importeert `nietUitleveren` in plaats van hem te
  herhalen.
- Naar links: `pack_web_release_test.dart` spiegelt de échte `web/`-boom
  in een nagebouwde bundel. Dat zet de controle op de per-PR-poort, waar
  geen webbouw draait en waar #1888 daarom groen doorheen kwam.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
brenno merged commit 40ae8b2a8b into main 2026-08-31 22:48:47 +00:00
Sign in to join this conversation.
No description provided.