pathsafe

package
v0.1.0-develop.2026060... Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 9, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package pathsafe is the single funnel for scaffold/codegen file writes. Containment + conflict detection + atomic write live here so the rest of the tree cannot bypass them by accident.

Design

All scaffold/codegen filesystem writes funnel through WritePlannedFiles. The caller builds a []PlannedFile slice (render phase), wraps it via NewPlanSet (which validates dup-AbsPath; PATHSAFE-PLANSET-TYPED-HARD-01), then calls WritePlannedFiles(realRoot, ps, dryRun) once (execute phase). This gives:

  • root containment via ResolveRoot + ContainPath before any write
  • all-or-nothing conflict detection (full plan checked before first write)
  • atomic write with best-effort rollback on failure (no half-written state)

AI-Hard contract

WritePlannedFiles is the only function in scaffold paths allowed to call os.MkdirAll / os.WriteFile. Direct calls in scaffold packages are statically forbidden by archtest SCAFFOLD-WRITE-FUNNEL-01.

Extension contract

When adding a new scaffold sub-package that writes files, add it to the archtest SCAFFOLD-WRITE-FUNNEL-01 scope list in tools/archtest/scaffold_write_funnel_test.go.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ContainPath

func ContainPath(realRoot, targetRel string) (string, error)

ContainPath returns the cleaned absolute path of targetRel under realRoot after asserting no existing parent symlink resolves outside realRoot. Pre: realRoot is the output of ResolveRoot.

Returns an error if:

  • targetRel is absolute
  • targetRel contains ".." segments that escape realRoot
  • any existing parent directory in the resolved path lies outside realRoot

Note: this is a caller-side early-reject check used during plan CONSTRUCTION (e.g., scaffold flag parsing). It accepts symlinks that resolve within realRoot. The authoritative TOCTOU defense at WRITE time is the fd-anchored openat chain in WritePlannedFiles, which rejects ALL parent symlinks regardless of destination. Callers MUST NOT treat a successful ContainPath as a guarantee that subsequent WritePlannedFiles will succeed for the same path.

func ResolveRoot

func ResolveRoot(root string) (string, error)

ResolveRoot returns root resolved through filepath.EvalSymlinks so that subsequent ContainPath comparisons are stable even when root itself is a symlink. Returns an error if root does not exist or cannot be evaluated.

func WriteFile

func WriteFile(realRoot, absPath string, content []byte, mode os.FileMode) error

WriteFile is the single-file shorthand for WritePlannedFiles. Creates parent directories if missing, runs containment + leaf-symlink rejection + O_NOFOLLOW write. Used by codegen.Write so derived-artifact writes share the same safety contract as scaffold writes.

Pre: realRoot is the output of ResolveRoot.

func WriteFileForce

func WriteFileForce(realRoot, absPath string, content []byte, mode os.FileMode) error

WriteFileForce writes content to absPath, overwriting any existing file. This is the codegen variant: generated files may already exist on disk and need to be replaced. The caller must have already verified the existing content is a generated file (governance.IsGoCellGenerated) before calling.

Internally routed through secureMkdirAllAndWrite (forceOverwrite=true) so the parent walk and leaf write share the fd-anchored TOCTOU defense with the plan funnel. On unix, parent symlinks fail closed via syscall O_NOFOLLOW|O_DIRECTORY; the existing leaf inode is removed via unlinkat relative to the parent fd (symlinks unlinked rather than followed).

realRoot must be non-empty and the output of ResolveRoot; an empty realRoot is rejected with ErrValidationFailed. The dropped/created dir list is discarded: WriteFileForce is the single-shot variant and is not transactional across multiple files.

func WritePlannedFiles

func WritePlannedFiles(realRoot string, ps PlanSet, dryRun bool) error

WritePlannedFiles is the SINGLE filesystem write entry for scaffold/codegen.

  1. Each entry's AbsPath is lexically verified against realRoot via planContainmentPass (path-prefix only); authoritative symlink rejection happens at writePass via the fd-anchored openat(O_NOFOLLOW) chain (unix) or O_EXCL leaf write (windows advisory).
  2. Each AbsPath must not already exist (conflict detection over the FULL plan — no partial-write semantics). DerivedOverwrite entries are exempt here but still pass the forceOverwritePreflightPass inode-kind gate so dry-run rejects exactly what live would (F2 parity).
  3. dryRun returns nil after steps 1-2 succeed (validation only, no write).
  4. Otherwise: mkdir all required directories, write all files, then on the FIRST failure remove every file and directory created during this call (best-effort rollback). Returns the original error wrapped with the rollback outcome.

Pre: realRoot is the output of ResolveRoot. ps may be empty (no-op).

AI-Hard contract: this is the only function in the project allowed to call os.MkdirAll / os.WriteFile in scaffold paths. All other call sites are statically rejected by archtest SCAFFOLD-WRITE-FUNNEL-01. The second parameter is PlanSet (not []PlannedFile) so the dup-reject invariant is established at the type boundary (PATHSAFE-PLANSET-TYPED-HARD-01).

Types

type PlanSet

type PlanSet struct {
	// contains filtered or unexported fields
}

PlanSet is the typed container of PlannedFile entries accepted by WritePlannedFiles. The internal slice is package-private, so the only way to obtain a PlanSet is through NewPlanSet (PATHSAFE-PLANSET-TYPED-HARD-01). This lifts the "no duplicate AbsPath in plan" invariant from a runtime Medium guard (the previous duplicatePass) to a type-system Hard guarantee: the WritePlannedFiles signature alone proves that no caller-supplied plan containing duplicates ever enters the funnel.

func NewPlanSet

func NewPlanSet(items []PlannedFile) (PlanSet, error)

NewPlanSet constructs a PlanSet, validating the plan content. Returns ErrConflict if any two entries share the same AbsPath. The input slice is defensively copied so post-construction caller mutation cannot break the invariant.

func (PlanSet) Len

func (ps PlanSet) Len() int

Len reports the number of plan entries.

func (PlanSet) Paths

func (ps PlanSet) Paths() []string

Paths returns the absolute target paths in plan order. Callers use this to surface dry-run output without exposing the underlying slice.

type PlannedFile

type PlannedFile struct {
	AbsPath  string
	Content  []byte
	DirMode  os.FileMode // optional; defaults 0o755 (per helm/helm chartutil; F12)
	FileMode os.FileMode // optional; defaults 0o644 (per helm/helm chartutil; F12)
	// contains filtered or unexported fields
}

PlannedFile pairs an absolute output path with its rendered content. Mode encodes file vs Go-source (Go-source files MAY round-trip through codegen.FormatGoSource at the caller before constructing PlannedFile — pathsafe is content-neutral by design).

The forceOverwrite field is package-private (PATHSAFE-FORCEOVERWRITE-TYPED-CTOR-01): callers outside pkg/pathsafe cannot set it via struct-literal; the only public path that produces a force-overwrite entry is DerivedOverwrite. This makes "regenerate semantics opted into by accident" unrepresentable at the type level.

func DerivedOverwrite

func DerivedOverwrite(absPath string, content []byte) PlannedFile

DerivedOverwrite is the SOLE public constructor that returns a PlannedFile with force-overwrite semantics: when fed to WritePlannedFiles, the entry bypasses conflictPass "file already exists" rejection and instead replaces the existing inode (Remove → O_EXCL|O_NOFOLLOW recreate). O_NOFOLLOW is preserved: a leaf symlink at the target path is still removed (not followed) before the fresh file is written.

pathsafe remains content-neutral; callers MUST enforce any content-source gate at the call site (e.g. cellgen's planDerivedArtifact runs governance.IsGoCellGenerated before invoking DerivedOverwrite; archtest SCAFFOLD-DERIVED-FORCEOVERWRITE-01 statically guards the cellgen call site).

func (PlannedFile) IsForceOverwrite

func (f PlannedFile) IsForceOverwrite() bool

IsForceOverwrite reports whether this PlannedFile was constructed via DerivedOverwrite (force-overwrite semantics). The setter (DerivedOverwrite) is the only public path that produces such an entry; this getter exists for test introspection so plan-builder regression tests can assert that derived artifacts are routed through the typed constructor. Production code MUST NOT branch on this getter — the flag is package-private precisely so flow control belongs to pathsafe itself.

For production introspection of derived-vs-skeleton routing, use DerivedOverwrite at construction time and route through WritePlannedFiles — the PlanSet boundary is the single decision point.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL