fix(policyfile): keep a repository source path inside the clone - #183
Open
tas50 wants to merge 1 commit into
Open
fix(policyfile): keep a repository source path inside the clone#183tas50 wants to merge 1 commit into
tas50 wants to merge 1 commit into
Conversation
tas50
force-pushed
the
fix/policyfile-git-path-traversal
branch
3 times, most recently
from
September 8, 2026 17:38
53b4083 to
5de4988
Compare
fetchGit joined the lock's "path" sub-option onto the clone directory with no containment check, while every other lock-derived path in the file goes through safeJoin. A path of "../.." resolves to any directory on the machine, and copyTree would pull whatever sits there into the cookbook cache. This is defence in depth, not a live fix. EnsureCookbook is fetchGit's only caller and reaches it through cinc-api's CookbookLock.Origin, which prefers the "path" key over "git" and classifies such a lock as a path source before fetchGit is ever called. The join is therefore protected only by key precedence in another module: reorder those keys upstream, or call fetchGit directly, and the guard disappears. It belongs next to the join. withinDir rather than safeJoin, because nested paths are legitimate here (the "cookbooks/<name>" monorepo layout); only escaping the clone is not. Signed-off-by: Tim Smith <tim@mondoo.com>
tas50
force-pushed
the
fix/policyfile-git-path-traversal
branch
from
September 8, 2026 17:42
5de4988 to
ebbdfbd
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is (and is not)
This is hardening, not a live vulnerability fix. I want to be straight about that up front, because I initially reported it as exploitable and was wrong.
fetchGitjoins the lock'spathsub-option onto the clone directory with no containment check:Every other lock-derived path in this file goes through
safeJoin— the cache key, the cookbook name, the identifier. This one does not, and apathof"../.."resolves anywhere on the machine.copyTreewould then pull whatever sits there into the cookbook cache, andcinc policy pushwould send it to the server.Why it is not reachable today:
EnsureCookbookis the only caller, and it dispatches oncinc-api'sCookbookLock.Origin(), which prefers thepathkey over the repository key:So a lock carrying both keys is classified as a path source and never reaches the fetch function under test. My original probe called that function directly and skipped this dispatch entirely.
Why fix it anyway
The join is currently protected only by key precedence in another module. Reorder those keys upstream, or add a second caller, and the guard silently disappears. A containment check belongs next to the join it protects, not in a dependency's iteration order.
The test shows the escape is otherwise real. On
mainthe path resolves outside the clone and is stopped only by themetadata.rbexistence check, which an attacker controlling the lock can satisfy:withinDirrather thansafeJoin, because nested paths are legitimate here (thecookbooks/<name>monorepo layout); only escaping the clone is not.Tests
TestFetchGitRejectsPathEscapingTheClonecovers../outsideandnested/../../outside. Both fail onmain.TestFetchGitAcceptsNestedPathconfirms the monorepo layout still resolves, so the guard is not over-tight.Both target the fetch function directly, since that is the unit carrying the defect.
Related, not fixed here
That same
Origin()precedence means a monorepo lock carrying both keys is silently treated as a local path source, which looks like a bug in its own right. It lives in cinc-api rather than this repo, so I have left it alone.go test ./...,go vet ./..., andgofmt -l .are clean.