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 with filepath.Join(root, …) where root is a caller-supplied parameter or field — with no filepath.EvalSymlinks anywhere on the path-producing chain — plus the archive twin: zip.Writer entry names assembled from a parameter with no path.Clean in the function.
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).
Silent postures, deliberately:
- any filepath.EvalSymlinks on the chain (writing function, or the same-package helper that produced the path) — the fix posture;
- roots that are not parameters or root/base/dir-named fields: a constant or computed root has no caller-controlled boundary to defend;
- joins whose every non-root argument is a literal: nothing caller-controlled is appended under the root;
- 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, and any function that calls path.Clean / filepath.Clean;
- reads (os.Open, os.ReadFile, O_RDONLY) by construction;
- _test.go files.
Narrowed 2026-09-02 after the whole-repo run: a direct Join must carry a caller-controlled (parameter-derived) component beside the root — formatted timestamps, counters, manifest literals, and generated ids are not escape vectors — and zip entry names must be COMPOSED from a parameter, not forwarded through a wrapper whose own callers compose. Calls to symlink-named guards (EnsureNoSymlinkPath) count as resolution. A same-package containment helper (rooty first parameter joined with the rest) feeding a write still fires when neither side resolves symlinks: that is the containedPath/Apply pair this rule was born from.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var Analyzer = &analysis.Analyzer{
Name: "gofastrrootwrite",
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.