You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
There are now two functions in the tree that answer "what is the canonical spelling of this path?", they are documented as if each were the only one, and they disagree on the exact case that caused #181.
Both were arrived at deliberately and both are currently correct for their own caller. The problem is that neither says it is one of two, so the next person to need canonicalisation will pick whichever they find first.
Why the divergences matter
Relative paths.paths.Canonical refuses to anchor them, because the daemon is a singleton whose working directory belongs to whichever client happened to spawn it — the silent cross-repository write of #181/#182. canonicalRoot anchors them with filepath.Abs. That is safe where it is used today (PathPolicy compares already-absolute roots), but it is exactly the wrong default to inherit by copy-paste.
Lexical ...canonicalPathForBoundary calls filepath.Abs before EvalSymlinks, and Abs Cleans — so a .. following a symlink is collapsed lexically, which diverges from the kernel's left-to-right resolution. This is the divergence #264 is landing a refusal for. paths.Canonical avoids it by passing the uncleaned path to EvalSymlinks, which resolves components in kernel order.
Decide explicitly what the absolute-anchoring behaviour should be at the boundary seam, rather than inheriting it. If PathPolicy genuinely needs a relative path anchored, it should do that itself, visibly, before calling the shared canonicaliser.
Add a rule to internal/arch.PrimitiveRules pinning the shared canonicaliser, so it cannot silently re-fork. PrimitiveRules exists for precisely this discipline and currently has no entry covering it.
Provenance
Split out of #270 (the #263 fix), which introduced paths.Canonical and deliberately did not touch internal/tools/boundary.go because #264 was in flight in that file and lands a property the graft must not undercut. Raised as a NIT by an independent review of #270, with the PrimitiveRules observation.
Not urgent and not a live defect — a trap for the next change, which is the cheapest kind of thing to fix before it fires.
Summary
There are now two functions in the tree that answer "what is the canonical spelling of this path?", they are documented as if each were the only one, and they disagree on the exact case that caused #181.
paths.Canonical(internal/paths/canonical.go)canonicalRoot/canonicalPathForBoundary(internal/tools/boundary.go)filepath.Abs..handlingEvalSymlinksuncleaned, so..resolves in kernel orderfilepath.AbsCleans first, collapsing..lexicallyfilepath.CleanBoth were arrived at deliberately and both are currently correct for their own caller. The problem is that neither says it is one of two, so the next person to need canonicalisation will pick whichever they find first.
Why the divergences matter
Relative paths.
paths.Canonicalrefuses to anchor them, because the daemon is a singleton whose working directory belongs to whichever client happened to spawn it — the silent cross-repository write of #181/#182.canonicalRootanchors them withfilepath.Abs. That is safe where it is used today (PathPolicycompares already-absolute roots), but it is exactly the wrong default to inherit by copy-paste.Lexical
...canonicalPathForBoundarycallsfilepath.AbsbeforeEvalSymlinks, andAbsCleans — so a..following a symlink is collapsed lexically, which diverges from the kernel's left-to-right resolution. This is the divergence #264 is landing a refusal for.paths.Canonicalavoids it by passing the uncleaned path toEvalSymlinks, which resolves components in kernel order.Suggested shape
Once #264 has landed:
canonicalRootdelegates topaths.Canonical, so there is one definition of "same place"...refusal stays where fix(tools): refuse an absolute path whose ".." the kernel resolves differently #264 puts it, inPathPolicy.Check, above the canonicaliser. That separation is the right one and must not be undone by the graft:Canonicalis an identity function, not an authorisation check.PathPolicygenuinely needs a relative path anchored, it should do that itself, visibly, before calling the shared canonicaliser.internal/arch.PrimitiveRulespinning the shared canonicaliser, so it cannot silently re-fork.PrimitiveRulesexists for precisely this discipline and currently has no entry covering it.Provenance
Split out of #270 (the #263 fix), which introduced
paths.Canonicaland deliberately did not touchinternal/tools/boundary.gobecause #264 was in flight in that file and lands a property the graft must not undercut. Raised as a NIT by an independent review of #270, with thePrimitiveRulesobservation.Not urgent and not a live defect — a trap for the next change, which is the cheapest kind of thing to fix before it fires.