Repository navigation
fix(security): refuse to mirror artwork through a symlinked destination - #1140
Merged
Merged
Conversation
mirrorArtworkUrls() finished with a bare writeFileSync(destination, ...) in both its SVG and non-SVG branches. writeFileSync() follows a symlink, so a link sitting on a mirror path under static/img/cncf-projects redirected the fetched third-party bytes to its target, outside the mirror directory. Those bytes then never reach the tree validate-architecture-assets.mjs scans, so the post-hoc asset gate cannot see the content that was written -- it can only see the symlink itself. That is the same argument the remote-reference check in this function was added on: gate the write here rather than relying on the asset validator to catch it after the bytes are on disk. mirrorLandscapeLogo() in scripts/lib/enduser-collector.mjs already refuses exactly this before its own write. Carry the check over, warning and skipping the URL to match every other failure path in this loop. Not currently exploitable: the repository has no committed symlinks and the asset validator rejects them, so the precondition is gated. This closes the ordering gap, where the write happens before validation and lands outside the scanned tree. Closes #1139 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: sec-check <sec-check@hive.kubestellar.io>
Contributor
Author
|
Important Held for human review by the hive's ACMM level gate. This PR was opened by the "sec-check" agent while Hive policy required a human checkpoint for that agent. Non-outreach agents are held at ACMM L3–L5; the Hive will keep the |
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.
Security Fix
mirrorArtworkUrls()inscripts/lib/architecture-content.mjsended both its SVG and non-SVG branches with a barewriteFileSync(destination, ...).writeFileSync()follows a symlink, so a link sitting on a mirror path understatic/img/cncf-projects/redirected the fetched third-party bytes to its target, outside the mirror directory.Those bytes then never reach the tree
scripts/validate-architecture-assets.mjsscans, so the post-hoc asset gate cannot see the content that was written — it can only see the symlink itself.Its sibling
mirrorLandscapeLogo()(scripts/lib/enduser-collector.mjs:215-216) already refuses exactly this before its own write. This carries the check over, warning and skipping the URL to match every other failure path in the loop.This is the same argument the
findRemoteReferences()gate in this very function was added on, whose comment still sits in the file: "Gate the write the way mirrorLandscapeLogo() does, rather than relying on validate-architecture-assets to catch it after the bytes are on disk." The remote-reference half was carried over; the symlink half was not.Honest severity
Not currently exploitable, and this PR does not claim it is. The precondition is a committed symlink under
static/img/cncf-projects/;git ls-files -sreports zero committed symlinks, andvalidate-architecture-assets.mjsrejects both symlinked entries (walk()) and symlinked asset roots (assetRootKind()). This closes the ordering gap — the write happens before validation and lands outside the scanned tree — so the fix is defence-in-depth.Scope
Exactly two files, claimed so the next comparison can see them:
scripts/lib/architecture-content.mjs—mirrorArtworkUrls()only (addslstatSyncimport + destination guard)tests/architecture-content-mirror.test.mjs— new fileNo overlap with the open held PRs: #1090 is
svg-active-content.mjs, #1122 ismdx-active-content.mjs, #1112 ispackage.json/package-lock.json.Verification
architecture-content-mirror,project-assets,import-architectures*,validate-architecture*): 158/158 pass.origin/mainand 52 with this change (pre-existing sandbox failures from a missing SWC native binding, unrelated to this diff); pass count rises 1527 → 1530, exactly the 3 tests added.prettier --checkandeslintclean on both files.Closes #1139
Filed by sec-check agent (ACMM L4/L5 — hold-gated mode). Hold-gated: human review required.
— hive: agent=sec-check backend=copilot model=claude-opus-5 copilot=1.0.88