Pfadschranke loest Symlinks auf - #8
Merged
Merged
Conversation
…leichen Ein Link im Projekt, der nach draussen zeigte, kam durch. Gemessen: ein Link "raus" auf /etc liess einen Write nach .../raus/hosts mit Exit 0 passieren. Dasselbe galt fuer einen harmlos benannten Link auf .claude/settings.json, die von innen eigentlich tabu ist. Der Hook loest jetzt beide Seiten physisch auf, bevor er vergleicht. Das Ziel eines Write existiert meist noch nicht, deshalb genuegt "cd && pwd -P" nicht: der Aufloeser spaltet ab, was es nicht gibt, laesst die Shell den tiefsten vorhandenen Vorfahren aufloesen und haengt die abgespaltenen Teile wieder an. Die koennen keine Links sein, sie existieren ja nicht. Ein Link als letzte Komponente wird eigens verfolgt, denn "cd" sieht ihn nicht, und genau darueber liefe sonst ein Write auf eine fremde Datei. Weil beide Seiten aufgeloest werden, faellt /tmp gegen /private/tmp gleich mit weg. Das ging bisher zugunsten der Sperre aus und wies einen Write auf genau den erlaubten Ordner ab. Vier neue Tests, jeder gegen einen echten Ordner mit echten Links: ein Link auf einen Ordner draussen, ein Link auf eine Datei draussen, ein Weg durch den Link in noch nicht existierendes Neuland, und ein Link auf die Hook-Konfiguration. Dazu die Gegenprobe, dass Schreiben im Projekt weiter laeuft, auch in noch nicht angelegte Unterordner. Zahnprobe: nimmt man dem Ziel die Aufloesung, oder der Wurzel, faellt jeweils die Tabelle. Mitgenommen, ohne Test: bei einem relativen Link im Wurzelverzeichnis entstand "//private/tmp/..." statt "/private/tmp/...". Auf das Urteil wirkt sich das nicht aus, weil beide Seiten denselben Weg gehen und den doppelten Schraegstrich gleichermassen bekommen; in den Meldungen stand er trotzdem. Er ist weg. Nicht geschlossen und an fuenf Stellen so dokumentiert: die Pruefung ist nicht atomar. Ein Link, der zwischen Pruefung und Schreibvorgang entsteht, wird vom Schreibvorgang verfolgt, und der Hook hat vorher geschaut. Dagegen hilft der Container, nicht der Hook. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BL7UoPomBp2Luhg89R6fYk
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ein Link im Projekt, der nach draußen zeigte, kam durch. Gemessen: ein Link
rausauf/etcließ einen Write nach.../raus/hostsmit Exit 0 passieren. Dasselbe galt für einen harmlos benannten Link auf.claude/settings.json, die von innen eigentlich tabu ist. Die Schranke verglich Schreibweisen, nicht Orte.Wie sie jetzt vergleicht
Beide Seiten werden physisch aufgelöst, bevor verglichen wird. Das Ziel eines Write existiert meist noch nicht, deshalb genügt
cd && pwd -Pallein nicht: der Auflöser spaltet ab, was es nicht gibt, lässt die Shell den tiefsten vorhandenen Vorfahren auflösen und hängt die abgespaltenen Teile wieder an. Die können keine Links sein, sie existieren ja nicht.Ein Link als letzte Komponente wird eigens verfolgt.
cdsieht ihn nicht, und genau darüber liefe sonst ein Write auf eine fremde Datei.Weil auch die Wurzel aufgelöst wird, fällt
/tmpgegen/private/tmpgleich mit weg. Das ging bisher zugunsten der Sperre aus und wies einen Write auf genau den erlaubten Ordner ab.Nachgemessen
Vier neue Tests, jeder gegen einen echten Ordner mit echten Links:
<projekt>/raus/hosts→ Exit 2<projekt>/fremde-datei→ Exit 2<projekt>/raus/neu/tief.txt→ Exit 2Dazu die Gegenprobe, dass Schreiben im Projekt weiter läuft, auch in noch nicht angelegte Unterordner, und dass eine Wurzel, die es noch gar nicht gibt, genauso behandelt wird wie eine vorhandene.
Zahnprobe: nimmt man dem Ziel die Auflösung, fällt die Tabelle. Nimmt man sie der Wurzel, fällt sie auch. 68 Tests, ein Skip (
docker compose, hier läuft kein Docker).Ein Nebenbefund, ehrlich eingeordnet
Bei einem relativen Link im Wurzelverzeichnis entstand
//private/tmp/...statt/private/tmp/.... Ich hielt das zuerst für verhaltensrelevant und habe einen Test dagegen geschrieben. Der Test hat nicht angeschlagen, und zu Recht: beide Seiten gehen denselben Weg und bekommen den doppelten Schrägstrich gleichermaßen, das Urteil ändert sich also nie. In den Meldungen stand er trotzdem, und weg ist er jetzt. Der Test heißt jetzt nach dem, was er wirklich zusichert.Was offen bleibt
Die Prüfung ist nicht atomar. Ein Link, der zwischen Prüfung und Schreibvorgang entsteht, wird vom Schreibvorgang verfolgt, und der Hook hat vorher geschaut. Dagegen hilft der Container, nicht der Hook. Das steht so in README, SECURITY.md, beiden SKILL.md und beiden erzeugten Setup-READMEs.
Ebenfalls unverändert offen: Schreiben über Bash geht weiter an den ersten Hook, der nur den Kommandotext liest.
🤖 Generated with Claude Code
https://claude.ai/code/session_01BL7UoPomBp2Luhg89R6fYk