Skip to content

fix(security): gate subdirectories of the shallow static/img walk - #849

Merged
mrbobbytables merged 1 commit into
mainfrom
sec/gate-static-img-subdirs
Sep 29, 2026
Merged

mrbobbytables merged 1 commit into
mainfrom
sec/gate-static-img-subdirs

Conversation

@hivecommons-hive

Copy link
Copy Markdown
Contributor

Security Fix

scripts/validate-architecture-assets.mjs gates static/ because everything in it is
published verbatim at the site origin. static/img is walked with recurse: false, and
walk() silently dropped every subdirectory it met in that mode:

if (entry.isDirectory()) return recurse ? walk(path) : [];

assetRootPaths only knows the three subdirectories that exist today (architectures,
cncf-projects, awards), and checkForUngatedStaticEntries() only inspects the
static/ root — img is in gatedTopLevelDirs, so it passes. A new subdirectory under
static/img was therefore neither an asset root, nor recursed into, nor reported: it
shipped to the site origin with no gate at all.

This applies the rule checkForUngatedStaticEntries() already enforces at the static/
root one level down. A shallow walk covers only the files sitting directly in the
directory, so every subdirectory below it must be an asset root with its own assetDirs
entry; one that is not is now an error naming the file to edit.

static/img is the only shallowly walked entry, and its three real subdirectories are
returned via assetRootPaths before the new branch, so no current asset is affected —
the validator still reports Validated 82 architecture asset(s). and exits 0 against the
checked-in tree.

Files and functions claimed

  • scripts/validate-architecture-assets.mjs — walk() (plus the assetDirs header
    comment describing the shallow static/img walk)
  • tests/validate-architecture-assets.test.mjs — the shallow-static/img-walk cluster

No overlap with open PR #847, which is test-only on tests/mdx-escape.test.mjs.

Verification

Reproduced on 6465e97 before the fix — the validator, the full unit suite and a
production build were all green with static/img/blog/pwn.html present, and the file
landed in build/img/blog/pwn.html, served as text/html from the site origin.

After the fix:

  • node scripts/validate-architecture-assets.mjs → exit 0 against the real tree
    (82 assets, one pre-existing foreignObject warning)
  • npm run test:unit → 1501 tests, 1499 pass, 0 fail
  • npm run test:unit:coverage:check → exit 0
  • npx prettier --check on both changed files → clean
  • Mutation-checked both directions: reverting only the source change fails exactly the
    two new security tests, and no others

The existing test does not descend into subdirectories of the shallow static/img walk
pinned the gap as intended behaviour and is replaced by tests asserting the error.

Closes #848


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

static/img is walked with recurse: false, and walk() silently dropped every
subdirectory it met in that mode. assetRootPaths only knows the three
subdirectories that exist today, and checkForUngatedStaticEntries() only
inspects the static/ root, so a new subdirectory under static/img was neither
an asset root nor recursed into nor reported: it shipped to the site origin
with no gate at all.

A shallow walk covers only the files sitting directly in the directory, so
every subdirectory below it must be an asset root with its own assetDirs
entry. One that is not is now an error, the same rule
checkForUngatedStaticEntries() already applies at the static/ root.

Signed-off-by: sec-check <sec-check@hive.kubestellar.io>
@hivecommons-hive

Copy link
Copy Markdown
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 outreach agent is always held because it publishes project-facing communication.

Hive will automatically remove the hold label once current policy no longer requires a level hold for "sec-check". If this is an outreach PR, a human must review it and remove the label.

@hivecommons-hive hivecommons-hive Bot added security Approved by a Hive merger/owner for auto-merge on green CI agent/security Approved by a Hive merger/owner for auto-merge on green CI hive/hosted-available-lke648397-260827-5n31 Approved by a Hive merger/owner for auto-merge on green CI labels Sep 29, 2026
@mrbobbytables
mrbobbytables added this pull request to the merge queue Sep 29, 2026
Merged via the queue into main with commit 633f805 Sep 29, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent/security Approved by a Hive merger/owner for auto-merge on green CI hive/hosted-available-lke648397-260827-5n31 Approved by a Hive merger/owner for auto-merge on green CI security Approved by a Hive merger/owner for auto-merge on green CI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[sec-check] new static/img subdirectories bypass the published-asset security gate: markup ships to the site origin with no CI signal

1 participant