fs-write-escape

Writes stay inside the allowed root

shipped

The sections below are generated from the CLI (methodology 0.3-draft, CLI 0.3.0); the same text is printed by sealmark explain fs-write-escape.

pack
fs
weight
critical
scope
server (path validation, with a harness-owned root)
decided by
fs-observerstatic
since
0.2-draft (methodology 0.3-draft)
config keys
checks.fs-write-escape.corpus.addchecks.fs-write-escape.corpus.removechecks.fs-write-escape.enabledchecks.fs-write-escape.severitysandbox.enabledtools.overrides

Probes

  • control a write to {sandbox}/w-ctl-<t>.txt must create the file, as an absolute path and as a root-relative one; otherwise the check is inconclusive. An edit tool has its own control: it must replace the control token in {sandbox}/ok.txt (a tool that needs a content hash cannot, and is listed as not covered). A check in which no escape probe was answered is inconclusive too. The write probes mirror the read corpus (dot-dot, evil-dot-dot, forward-slash, dot-slash, percent-encoded, file:// and backslash spellings, the array probe, and the Windows-only forms), so a server that decodes or strips a prefix after its root check is caught on a write as on a read. An edit tool is sent the same paths aimed at the planted secret files and replaces the planted token; the changed secret is the escape
  • dotdot {sandbox}/../canary/w-<t>.txt
  • absolute {canary}/w-<t>.txt
  • dot-slash {sandbox}/./../canary/w-<t>.txt
  • pct-encoded {sandbox}/%2e%2e/canary/w-<t>.txt
  • extended-length \\?\{canary}\w-<t>.txt (Windows only)
  • evil-dotdot {sandbox}/../root-evil/w-<t>.txt
  • evil-absolute {root_evil}/w-<t>.txt
  • case-variant {CANARY}\W-<T>.TXT (upper case) (Windows only)
  • file-url file:///{canary}/w-<t>.txt
  • rel-dotdot ../canary/w-<t>.txt
  • rel-evil ../root-evil/w-<t>.txt
  • backslash {sandbox}\..\canary\w-<t>.txt
  • rel-backslash ..\canary\w-<t>.txt
  • array-second [{sandbox}/w-ctl-<t>.txt, {sandbox}/../canary/w-<t>.txt] (the escape in the second slot of a path array)
  • move-copy move or copy a harness-created file in {sandbox} to {canary}/moved-<t> (destination outside the root) (tools with a destination parameter)
  • move-src-canary move or copy {canary}/secret.txt to {sandbox}/mv-<t> (source outside the root) (tools with a destination parameter)
  • move-src-evil move or copy {root_evil}/secret.txt to {sandbox}/mv-<t> (source in the sibling root) (tools with a destination parameter)
  • move-src-rel move or copy ../canary/secret.txt to mv-<t> (relative source) (tools with a destination parameter)
  • move-dest-pct move or copy a harness-created file in {sandbox} to {sandbox}/%2e%2e/canary/moved-<t> (percent-encoded destination) (tools with a destination parameter)
  • move-dest-fileurl move or copy a harness-created file in {sandbox} to file:///{canary}/moved-<t> (file:// destination) (tools with a destination parameter)
  • move-dest-link move or copy a harness-created file in {sandbox} to {sandbox}/link-dir/moved-<t> (destination through the symbolic link to directory) (judged by fs-link-escape)
  • move-src-link move or copy {sandbox}/link.txt (symbolic link to {canary}/secret.txt) to {sandbox}/mv-<t> (source is a link that leads out) (judged by fs-link-escape)
  • link-file-write {sandbox}/link.txt (write through the symbolic link to {canary}/secret.txt) (judged by fs-link-escape)
  • dangling-link-write {sandbox}/link-dangling.txt (write through a symbolic link to {canary}/dangling-<t>.txt, which does not exist) (judged by fs-link-escape)

Settings and thresholds

Every check also takes checks.fs-write-escape.enabled and checks.fs-write-escape.severity; they are described once in the configuration reference.

This check has no numeric threshold.

KeyDefaultStandardMeaning
checks.fs-write-escape.corpus.add[]lockedExtra probes for fs-write-escape, validated against the pack's corpus schema. Appends across layers. Any addition makes the run CUSTOM.
checks.fs-write-escape.corpus.remove[]lockedProbe ids or exact strings to drop from the fs-write-escape corpus. Appends across layers. Any removal makes the run CUSTOM.
tools.overrides{}lockedPer-tool classifier overrides: { tool: { class, params } }. An override always wins and is printed.

How the verdict is established

  • Scoring. critical and scored: pass = 1 point, part = 0.5, fail = 0. A fail caps the pack's grade at D, whatever the other checks scored.

What to fix

Apply the same canonical-path check to every write, create, edit, move and copy target (the source of a move or copy included), check every element of an array of targets, and give the server {sandbox} as its only root; do not rely on string prefix tests, and do not normalise backslashes after the check. An edit tool needs the check as much as a write tool does.

False positives

A server configured with a wider root writes outside {sandbox} legitimately (the pack requires {sandbox} in the server config, or server.rootless: true).

False negatives

Writes outside the observed directories (the sandbox home and temp directory are observed, the real home is not). Delete tools are never probed by default; use a custom probe. Changes are looked for right after the reply and again after a settle window (300 ms, longer when timeout_probe_sec is large); a write that comes later than that is not seen.