From fcb132eca0f988f99e6b399411b8dddc09678308 Mon Sep 17 00:00:00 2001 From: kiannidev <156195510+kiannidev@users.noreply.github.com> Date: Thu, 25 Jun 2026 16:22:24 +0200 Subject: [PATCH] fix(signals): redact /root/ local paths on the public-safety boundary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The signals public/private boundary treats /Users/, /home/, and /tmp/ (and Windows ...\Users\...) as local filesystem paths that must not reach public GitHub surfaces, but missed /root/ — the root user's home directory. A contributor running local branch analysis from a /root/... working tree (common in containers/CI/devcontainers) could leak that absolute path. The project already treats /root/ as a local path in miner-dashboard-recommendations.ts; this aligns the canonical boundary primitive (redaction.ts PUBLIC_UNSAFE_PATTERN) and the changed-file-path redactor (local-branch.ts safeRepoPath) with that intent. Behavior-preserving for all existing inputs; only adds /root/ detection. Fixes #1375 Co-authored-by: Cursor --- src/signals/local-branch.ts | 2 +- src/signals/redaction.ts | 2 +- test/unit/local-branch.test.ts | 23 +++++++++++++++++++++++ test/unit/redaction.test.ts | 2 ++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/signals/local-branch.ts b/src/signals/local-branch.ts index 98c5cde15..e3de63d47 100644 --- a/src/signals/local-branch.ts +++ b/src/signals/local-branch.ts @@ -1228,7 +1228,7 @@ function firstCommitTitle(messages: string[] | undefined): string | undefined { function safeRepoPath(path: string): string { /* v8 ignore next -- Empty path fallback protects malformed local-git adapters; path redaction is covered by local branch tests. */ - return /^(\/Users\/|\/home\/|\/tmp\/|[A-Z]:\/Users\/)/i.test(String(path).replace(/\\/g, "/")) ? "[local path hidden]" : String(path || "(unknown path)").replace(/\\/g, "/"); + return /^(\/Users\/|\/home\/|\/root\/|\/tmp\/|[A-Z]:\/Users\/)/i.test(String(path).replace(/\\/g, "/")) ? "[local path hidden]" : String(path || "(unknown path)").replace(/\\/g, "/"); } export function isTestFile(file: string): boolean { diff --git a/src/signals/redaction.ts b/src/signals/redaction.ts index 7b6e7af8a..cb75a85db 100644 --- a/src/signals/redaction.ts +++ b/src/signals/redaction.ts @@ -19,7 +19,7 @@ // intentionally NOT collapsed onto `PUBLIC_UNSAFE_TERMS`. export const PUBLIC_UNSAFE_TERMS = String.raw`reward\w*|score\w*|wallet|hotkey|coldkey|mnemonic|farming|payout|ranking|raw[-_\s]?trust|trust[-_\s]?score|private[-_\s]?reviewability|reviewability`; -export const PUBLIC_UNSAFE_PATTERN = new RegExp(String.raw`\b(${PUBLIC_UNSAFE_TERMS})\b|/Users/|/home/|/tmp/|[A-Z]:[\\/]Users[\\/]`, "i"); +export const PUBLIC_UNSAFE_PATTERN = new RegExp(String.raw`\b(${PUBLIC_UNSAFE_TERMS})\b|/Users/|/home/|/root/|/tmp/|[A-Z]:[\\/]Users[\\/]`, "i"); /** True iff `text` contains nothing that must stay private — i.e. it is safe to surface on a public GitHub surface. */ export function isPublicSafeText(text: string): boolean { diff --git a/test/unit/local-branch.test.ts b/test/unit/local-branch.test.ts index afdd92b0f..a0e2eb7bc 100644 --- a/test/unit/local-branch.test.ts +++ b/test/unit/local-branch.test.ts @@ -1147,6 +1147,29 @@ describe("local branch analysis", () => { expect(analysis.prPacket.markdown).not.toMatch(/C:\\Users\\alice/i); }); + it("hides root-user home paths from public PR packet changed paths", () => { + const analysis = buildLocalBranchAnalysis({ + input: { + login: "oktofeesh1", + repoFullName: repo.fullName, + body: "Fixes #7", + changedFiles: [{ path: "/root/work/src/cache.ts", additions: 12, deletions: 2, status: "modified" }], + validation: [{ command: "npm test -- cache", status: "passed" }], + }, + repo, + issues: [{ repoFullName: repo.fullName, number: 7, title: "Cache refresh fails", state: "open", labels: ["bug"], linkedPrs: [] }], + pullRequests: [], + profile, + outcomeHistory, + scoringSnapshot, + scoringProfile, + }); + + expect(analysis.prPacket.markdown).toContain("## Changed Paths"); + expect(analysis.prPacket.markdown).toContain("[local path hidden]"); + expect(analysis.prPacket.markdown).not.toContain("/root/work"); + }); + it("removes snake_case private signals from public PR packet markdown", () => { const analysis = buildLocalBranchAnalysis({ input: { diff --git a/test/unit/redaction.test.ts b/test/unit/redaction.test.ts index 6be22518b..780e431a2 100644 --- a/test/unit/redaction.test.ts +++ b/test/unit/redaction.test.ts @@ -32,6 +32,8 @@ describe("isPublicSafeText (#542 shared public/private boundary)", () => { it("rejects local filesystem paths (posix and Windows)", () => { expect(isPublicSafeText("/Users/alice/project")).toBe(false); expect(isPublicSafeText("/home/bob/repo")).toBe(false); + expect(isPublicSafeText("/root/project/src")).toBe(false); + expect(isPublicSafeText("clone failed at /root/work/repo")).toBe(false); expect(isPublicSafeText("/tmp/scratch")).toBe(false); expect(isPublicSafeText("C:\\Users\\carol\\repo")).toBe(false); expect(isPublicSafeText("C:/Users/carol/repo")).toBe(false);