From 8d1047fd1c65323f278fee727a311406839f5a1d Mon Sep 17 00:00:00 2001 From: AyobamiH Date: Thu, 30 Jul 2026 22:05:23 +0100 Subject: [PATCH] fix: skip unreadable secret-audit paths --- scripts/lib/secret-audit.mjs | 17 +++++++++++++++-- scripts/test-pack.mjs | 29 +++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/scripts/lib/secret-audit.mjs b/scripts/lib/secret-audit.mjs index 0df66b7..3d3d04b 100644 --- a/scripts/lib/secret-audit.mjs +++ b/scripts/lib/secret-audit.mjs @@ -311,13 +311,26 @@ function walkPath(projectRoot, absolute, ignoredPaths, files, skipped) { skipped.push({ path: relative, reason: "secret-bearing path excluded" }); return; } - const stat = fs.lstatSync(absolute); + let stat; + try { + stat = fs.lstatSync(absolute); + } catch { + skipped.push({ path: relative, reason: "unreadable path" }); + return; + } if (stat.isSymbolicLink()) { skipped.push({ path: relative, reason: "symbolic link skipped" }); return; } if (stat.isDirectory()) { - for (const entry of fs.readdirSync(absolute)) { + let entries; + try { + entries = fs.readdirSync(absolute); + } catch { + skipped.push({ path: relative, reason: "unreadable directory" }); + return; + } + for (const entry of entries) { walkPath(projectRoot, path.join(absolute, entry), ignoredPaths, files, skipped); } return; diff --git a/scripts/test-pack.mjs b/scripts/test-pack.mjs index 83025fa..cc691d2 100644 --- a/scripts/test-pack.mjs +++ b/scripts/test-pack.mjs @@ -896,6 +896,35 @@ test("secret-audit reports high-confidence findings without printing values", () assert.doesNotMatch(rendered, new RegExp(syntheticSecret)); }); +test("secret-audit records and skips an unreadable directory", () => { + if (process.platform === "win32" || (typeof process.getuid === "function" && process.getuid() === 0)) { + return; + } + const temporary = fs.mkdtempSync(path.join(os.tmpdir(), "secret-audit-unreadable-")); + const readable = path.join(temporary, "src"); + const unreadable = path.join(temporary, "protected"); + fs.mkdirSync(readable); + fs.mkdirSync(unreadable); + fs.writeFileSync(path.join(readable, "config.ts"), 'export const mode = "safe";\n'); + fs.writeFileSync(path.join(unreadable, "hidden.ts"), 'export const hidden = "not-read";\n'); + fs.chmodSync(unreadable, 0o000); + + try { + const result = buildSecretAuditReport(temporary, { coreRoot: root }); + assert.equal(result.status, "complete"); + assert.ok(result.filesScanned.includes("src/config.ts")); + assert.ok(!result.filesScanned.includes("protected/hidden.ts")); + assert.ok( + result.skipped.some( + (item) => item.path === "protected" && item.reason === "unreadable directory", + ), + ); + } finally { + fs.chmodSync(unreadable, 0o700); + fs.rmSync(temporary, { recursive: true, force: true }); + } +}); + test("secret-audit respects adapter-declared scope", () => { const result = buildSecretAuditReport( path.join(root, "tests", "fixtures", "secret-audit", "adapter-project"),