Documentation
¶
Overview ¶
Package rootwrite catches writes whose containment under a root is resolved lexically only: os.WriteFile / os.Create / os.OpenFile(write flag) / os.MkdirAll on a path built under a root — filepath.Join, or a flat `root + "/" + x` concatenation — where the root is a caller-supplied parameter or field — with no filepath.EvalSymlinks on that path's chain — plus the archive twin: zip.Writer entry names assembled from a parameter with no path.Clean on the entry-name chain.
The bug class: lexical prefix checks cannot see symlinks. Probes TestApplyRefusesSymlinkEscape (framework/contracts report.go containedPath/Apply, fixed in 77fdbaf4: a diagnostic whose path crossed a symlinked directory was written outside the project root even though Join+HasPrefix said "contained") and TestPackZipPrefixCannotEscapeDir (framework/sdk zip.go PackZip, fixed in 1501a555: a "../" prefix placed archive entries above the target directory on extract).
Every gate is per write and on the write's own dataflow: resolution, validation, and cleaning on an unrelated path (or consulted for a boolean and leaving the path components untouched) gate nothing.
Silent postures, deliberately:
- the write's path (or a component of it, or the Join's root) is bound to a filepath.EvalSymlinks result, or an EvalSymlinks ran on this path expression — resolution on the chain (the fix posture);
- calls to symlink-named guards (EnsureNoSymlinkPath): resolution by another name;
- a sanitizer/validator whose RESULT replaces a joined component (sanitize(name) feeding the Join): the component that reaches the disk passed through it. A validator consulted for its boolean cannot see symlinks and gates nothing;
- roots that are not parameters or root/base/dir-named fields (a constant or computed root has no caller-controlled boundary to defend), including a rooty local resolved through one;
- builds whose every non-root argument is a literal — nothing caller-controlled is appended under the root. This holds at the helper hop too: a same-package containment helper called with literal-only non-root arguments stays quiet, and a helper whose body resolves symlinks is the fix posture;
- temp roots: a local bound to os.MkdirTemp or t.TempDir is throwaway by construction;
- zip entry names assembled only from literals or non-parameter values (a wrapper forwarding its own name parameter composes nothing), and entry names whose assembly is bound to a path.Clean / filepath.Clean result;
- reads (os.Open, os.ReadFile, O_RDONLY) by construction;
- _test.go files.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var Analyzer = &analysis.Analyzer{
Name: "rootwrite",
Doc: "forbids writes under a root whose containment is lexical only: resolve with filepath.EvalSymlinks, and path.Clean zip entry names",
Run: run,
}
Functions ¶
This section is empty.
Types ¶
This section is empty.