I2151 — Stránka obsluhovaná vedle zapisovacích tras spouští jen skript, který dodala, a hostitel s přesměrovanou vazbou nic nečte
Dát /swagger bezpečnostní hlavičky Cockpitu a skripty s kontrolou integrity, odmítnout GET, jehož Host jmenuje jiný server, a escapovat sekvence rozbíjející skripty tam, kde web vkládá JSON.
HOTOVO vlna 0 · p0 · profil implementation · běží samostatně
Cíl
Dát /swagger bezpečnostní hlavičky Cockpitu a skripty s kontrolou integrity, odmítnout GET, jehož Host jmenuje jiný server, a escapovat sekvence rozbíjející skripty tam, kde web vkládá JSON.
Proč
/swagger neservíruje žádnou Content-Security-Policy, žádné frame-ancestors a žádnou subresource integrity pro skripty, které načítá z unpkg (apps/majordomus-cli/src/http/swagger.rs:52,79; hlavičky potvrzeny živě dílčím auditem), na stejném původu jako každá trasa POST. Kontrola Origin (apps/majordomus-cli/src/http/router.rs:503-508, 718-745) projde pro skript ze stejného původu, takže podvržený skript z CDN by mohl volat plan/transition, continuity/*, recover/orphans nebo executions/start. Trasy GET neprovádějí validaci Host (kontroluje se jen jiné než GET, apps/majordomus-cli/src/http/router.rs:504, host_is_literal na :749) a komentář v apps/majordomus-cli/src/http/router.rs:498-502 předpokládá, že čtení z jiného původu je omezeno, což DNS rebinding porušuje (exploit je neověřený). Statický web vkládá json_encode | safe do prvků script bez escapování uzavírací sekvence (site/templates/partials/graph.html:76, site/templates/api.html:94, site/templates/why-section.html:284; zneužitelnost neověřena). Cockpit už CSP posílá (apps/majordomus-cli/src/cockpit/mod.rs:193-204, 296-310). Základní audit z 2026-10-09 na masteru 1fb8d6be44, mezery projekcí 7 a 11, řádek autorizace životního cyklu a drift 9.
Aktuální stav
/swagger důvěřuje CDN s původem zapisovacích tras; GET odpovídá na jakýkoli Host; tři šablony webu vkládají JSON bez escapování.
Cílový stav
/swagger se servíruje s vlastní Content-Security-Policy (připnuté soubory z unpkg a jediný inline skript podle digestu, frame-ancestors none, žádný eval) plus nosniff a no-referrer a oba soubory z CDN nesou integritu sha384; požadavek z loopbacku, jehož Host není ani adresa, ani localhost, je odmítnut s 403 forbidden dřív, než odpoví jakýkoli povrch (stránka s rebindingem nic nepřečte), zatímco požadavek z jiného hostitele může tento stroj pojmenovat tak, jak se zná; tři šablony webu nahrazují </ za <\/ před označením JSON jako bezpečného. Ověřeno v Chromiu: /swagger vykreslí 53 skupin operací bez chyby v konzoli a bez porušení CSP.
Rozsah
- apps/majordomus-cli/src/http/swagger.rs
- apps/majordomus-cli/src/http/router.rs
- apps/majordomus-cli/src/cockpit/mod.rs
- site/templates/partials/graph.html
- site/templates/api.html
- site/templates/why-section.html
- apps/majordomus-cli/tests/http_host.rs
- apps/majordomus-cli/tests/http_serve.rs
- test/cases
- docs/MCP.md
Závislosti
Žádné. Tento úkol je kořenem grafu.
Kritéria přijetí
- GET /swagger odpovídá hlavičkou Content-Security-Policy, která obsahuje frame-ancestors, a každý skript, který načítá, nese atribut integrity
- GET, jehož hlavička Host jmenuje jinou doménu, je odmítnut dříve, než se spustí jeho handler
- Hodnota dat webu obsahující uzavírací značku script se vykreslí jako text v každé šabloně, která vkládá JSON
Validace
- bin/majordomus plan validate
- cargo test --test http_host --test http_serve
- MJ_TEST_JOBS=4 bash test/run.sh <the case this issue adds>
Požadované důkazy
- swagger_csp_test
- rebinding_host_refused_test
- site_script_escape_case
Důkazy
| pokrývá | typ | command | výsledek | u commitu |
|---|---|---|---|---|
| site_script_escape_case | test | MJ_TEST_JOBS=1 bash test/run.sh 3043_site_json_cannot_close_its_script | 1 passed at c0e3585dc83: každý ze souborů partials/graph.html, api.html a why-section.html escapuje uzavírací sekvenci před označením JSON za bezpečný a řetězec vykreslený zola 0.23 nad hodnotou nesoucí uzavírací značku script se uzavře jednou a čte se zpět beze změny; po odebrání escapování z api.html případ selže | c0e3585 |
| rebinding_host_refused_test | ci | cargo test --test remote_callers a_loopback_request_under_a_foreign_host_name_reads_nothing (validate run 38626102499, job rust (3) 116065669794) | ok na masteru e8505a431d5: loopbackový GET adresovaný na attacker.example:8741 dostane 403 forbidden dřív, než cokoli čte kterákoli plocha (router.rs rebound(), dodáno 21ea43629ce) | c0e3585 |
| swagger_csp_test | ci | cargo test --test remote_callers the_api_page_runs_only_the_scripts_it_ships (běh validate 38626102499, úloha rust (3) 116065669794) | ok on master e8505a431d5: /swagger carries a Content-Security-Policy with frame-ancestors none and no unsafe-eval, and each CDN script carries sha384 integrity (swagger.rs, delivered by 21ea43629ce) | c0e3585 |
Časová osa
- zahájeno
- 2026-10-14T22:16:25Z
- ověřeno
- 2026-10-14T22:16:45Z
- dokončeno
- 2026-10-14T22:17:04Z
Tato tři pole, důkazy výše a stav závislostí jsou vše, z čeho se stav skládá. Neexistuje žádné pole stavu, které by s nimi mohlo být v rozporu.
Kanonický záznam: .ai/repo/project/issues/I2151.yaml. Přečtěte ho zpět pomocí majordomus plan show I2151.