Hub je místní aplikace, která umí spouštět shell. Tenhle soubor říká, čím je to ohraničené a co se v revizi našlo a opravilo — ať je vidět, na čem to stojí, a ať je z čeho vyjít při další změně.
Poslouchá jen na 127.0.0.1 |
na náhodném portu, nikdy na 0.0.0.0 (o telefonu níž) |
| Token na každý požadavek | 24 bajtů z secrets.token_urlsafe, porovnává se compare_digest; stránka ho dostane v URL při startu |
Websocket navíc kontroluje Origin |
když hlavičku pošle prohlížeč, musí sedět na adresu, na kterou listener odpovídá |
| Nic se neposílá ven | jediné spojení do světa je kontrola verze na GitHubu a stažení aktualizace |
Token je celá obrana: bez něj vrací každý endpoint 403. Cizí stránka v prohlížeči na port dosáhne, ale token neuhodne a odpověď si kvůli CORS stejně nepřečte.
Zapíná se v nastavení a dokud se nezapne, žádný druhý listener nevzniká. Když se zapne, platí navíc:
Nikdy na 0.0.0.0 |
buď 127.0.0.1 a před tím tailscale serve (tailscaled drží TLS), nebo přímo adresa 100.x z tailnetu. Mimo tailnet žádný listener není. |
| Vlastní dlouhodobý token | 24 bajtů z secrets.token_urlsafe v hub-config.json, oddělený od tokenu okna. Tlačítko „Odpojit telefon" ho vymění a spárované telefony odhlásí. |
| Token v cookie | HttpOnly, SameSite=Lax, Secure za https — aby se QR kód skenoval jen poprvé. Nastaví se, jen když token dorazil v URL, tedy po naskenování kódu. |
Origin z cizí stránky neprojde |
povolené jsou adresy, na kterých listener běží, plus vlastní Host požadavku |
| Odpárovaný telefon dostane vysvětlení | místo holého 403 stránka „naskenuj QR znovu"; aplikace pro Android se na ni rovnou přepne do párování |
Za tenhle kus zodpovídá Tailscale: kdo není v tailnetu, nemá se kam připojit. Token je druhá vrstva pro případ, že by se do tailnetu dostal někdo cizí.
Příkaz pro nový tab se skládal jako text a cesta se do něj vkládala v dvojitých
uvozovkách. Ty ale nezastaví $(…) ani zpětné apostrofy — takže složka
pojmenovaná
projekt$(rm -rf ~)x
by svůj obsah spustila ve chvíli, kdy na ni člověk v panelu klikne. Takové jméno přitom nevznikne jen naschvál: stačí naklonovat cizí repo nebo rozbalit archiv.
Cesty teď procházejí sh_quote(), tedy jednoduchými uvozovkami, ve kterých má
význam jediný znak — apostrof sám. Ověřeno na $( ), zpětných apostrofech,
středníku i uvozovce; a zvlášť na tom, že běžná cesta i cesta s apostrofem
a mezerou se pořád otevře.
Adresa repa pro stažení paměti šla do git clone tak, jak přišla. Řetězec
začínající pomlčkou ale git nevezme jako adresu, nýbrž jako přepínač —
a --upload-pack=… umí spustit cizí příkaz. Adresa teď musí projít sítem
(REPO_RE), nesmí začínat pomlčkou a do příkazu jde až za --. Totéž platí
pro jméno nového repa předávané gh.
Odkaz ve výpisu se dá kliknutím otevřít systémovým handlerem, a výpis může
pocházet z cizího repa. file://….desktop by se pod tím handlerem klidně
spustil. Pouštějí se proto jen http://, https://, mailto: a obsidian://;
místní cesta se otevře, jen když opravdu existuje.
Briefing se ukládá do CLAUDE.md projektu. Zapisovat kamkoli na disk není
potřeba, takže cíl musí být složka, kterou hub zná jako projekt. Do cizího
obsahu se nesahá — blok je ohraničený značkami a jen se vyměňuje.
- Statika: shoda cesty se ověřuje přes
commonpath, nestartswith— ten by pustil i sourozence jménemstatic-cokoliv. - Rozbalení aktualizace:
extractalls filtremdata, se starším Pythonem ručně; archiv nesmí zapsat mimo cílovou složku ani obsahovat odkazy. - Nahrané soubory: jméno se očistí, přípona musí být z bílé listiny, strop
25 MB, cíl vždy
~/.claude/hub-images. hub-projects.jsonse zapisuje s právy600— briefingy bývají o klientech.
Držitel tokenu je uživatel sám, takže hub úmyslně umí věci, které by jinde byly podezřelé: spustit shell, číst a zapisovat schránku, vypsat libovolnou složku (výběr složky), přesunout vault, stáhnout a nainstalovat vlastní aktualizaci. Ohraničené je to tím, kdo se k tokenu dostane — ne tím, co s ním jde dělat.
Žádná data, žádné projekty, žádná paměť, žádné přihlašovací údaje. Osobní věci
ze ~/.claude (settings.json, .credentials.json, .ftp-deploy.json) jsou
v .gitignore a instalačka je nikdy nekopíruje ven.
Napiš do issues repa. Když jde o něco, co by se nemělo objevit veřejně, pošli to rovnou majiteli repa.