Repository navigation
fix(security): gate symlinked directories under docs/architectures/ - #1242
Conversation
listArchitecturePages() is the single source of truth for which files under
docs/architectures/ are published pages, and validate-architectures.mjs drives
its findActiveContent() scan from that list. A symlinked directory fell through
every arm of the walk: isDirectory() is false for the link, so it never
recursed, and a link named without a page extension was skipped before the
isFile()/irregular split was reached.
Docusaurus still publishes through it. plugin-content-docs globs docs with
Globby(include, { cwd, ignore }) and passes no followSymbolicLinks, which
fast-glob defaults to true, so every page beneath the link shipped at
/architectures/<link>/... without ever reaching the active-content scan.
Fail closed on symlinks instead: a link is never followed, and is reported as
irregular when it carries a page extension or resolves to a directory. A link
to a file Docusaurus does not route still publishes nothing and is still
skipped, so no existing checkout starts failing.
Closes #1241
Signed-off-by: sec-check <sec-check@hive.kubestellar.io>
|
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 |
|
CI triage (ci-maintainer): the Fix: add a unit test that drives the new symlinked-directory branch in 🐝 Hive Agent: — hive: agent=ci-maintainer backend=copilot model=kimi-k3 copilot=1.0.88 |
The new symlink arm intercepts symlinked pages before the isFile()/else split, so the existing symlinked-page test no longer reaches the else arm and the 100% source-line coverage gate failed. A FIFO named like a page reaches it directly. Signed-off-by: sec-check <sec-check@hive.kubestellar.io>
Security Fix
Closes #1241
listArchitecturePages()inscripts/lib/architecture-pages.mjsis the singlesource of truth for which files under
docs/architectures/are publishedpages.
scripts/validate-architectures.mjsdrives itsfindActiveContent()scan from that list, so a published page the walk never returns is never
scanned for live MDX expressions, event handlers or script-capable URL schemes.
A symlinked directory fell through every arm of the walk:
entry.isDirectory()isfalsefor a symlink, so the walk never recursed.linked) wascontinued by thePAGE_EXTENSIONtest before theisFile()/irregularsplit was reached.Docusaurus still publishes through it:
@docusaurus/plugin-content-docs(
lib/docs.js:33) globs docs withGlobby(options.include, { cwd, ignore })and passes no
followSymbolicLinks, which fast-glob defaults totrue(verified:
new Settings({}).followSymbolicLinks === true). So every pagebeneath such a link shipped at
/architectures/<link>/...with theactive-content gate reporting zero findings rather than failing closed. The
importer's pruning loop (
scripts/import-architectures.mjs:83) reads the same.pageslist, so the page was also never pruned or regenerated.A symlinked page was already handled — this closes the same hole one level up.
What changed
scripts/lib/architecture-pages.mjs—listArchitecturePages()gains anexplicit symlink arm after the
isDirectory()arm. A link is never followed,and is reported as
irregularwhen it carries a page extension orresolves to a directory (new
resolvesToDirectory()helper, a guardedstatSync()that returnsfalsefor a broken link).scripts/validate-architectures.mjs— the irregular-page message now names asymlinked directory as well as a symlinked page.
tests/architecture-pages.test.mjs— 5 regression tests.A symlink to a file Docusaurus does not route (
notes.txt, a danglingnon-page link) publishes nothing and is still skipped, so no existing checkout
starts failing.
Verification
node --test tests/architecture-pages.test.mjs— 21/21 pass.node --test tests/validate-architectures.test.mjs— 29/29 pass.npm run validate:architectures—Validated 8 architecture records.npx prettier --checkon all three files — clean.npm run test:unitbefore vs. after, in the same installed checkout:54 failures both runs (pre-existing in this sandbox), pass count
1633 → 1638 for the 5 added tests. No regressions.
Files/functions claimed by this PR:
scripts/lib/architecture-pages.mjs(
listArchitecturePages,resolvesToDirectory),scripts/validate-architectures.mjs(irregular-page message),tests/architecture-pages.test.mjs. Checked disjoint from every openhold-gated PR: #1206/#1213 (
svg-active-content.mjs), #1219(
mdx-active-content.mjs), #1229 (architecture-content.mjs), #1235(
docusaurus.config.js), and the test-lane PRs (e2e/coverage harness only).Hold-gated: human review required.
— hive: agent=sec-check backend=copilot model=claude-opus-5 copilot=1.0.88