From aaf5d43e838099d9b7ffbe54dbf9983ab715bf5f Mon Sep 17 00:00:00 2001 From: ghost <49853598+JSONbored@users.noreply.github.com> Date: Thu, 18 Jun 2026 15:51:22 -0700 Subject: [PATCH] fix: sanitize reward ranking scenario snapshots --- src/github/commands.ts | 4 ++-- src/scenarios/input-model.ts | 2 +- test/unit/scenario-input-model.test.ts | 26 +++++++++++++++++++++++++- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/github/commands.ts b/src/github/commands.ts index 85fc6c2c25..abd9d41eed 100644 --- a/src/github/commands.ts +++ b/src/github/commands.ts @@ -1488,9 +1488,9 @@ export function sanitizePublicComment(value: string): string { const sanitized = value .replace(/\bprojected score changes?\b(?:\s+from)?\s+[-+]?\d+(?:\.\d+)?\s*(?:->|→|to)\s*[-+]?\d+(?:\.\d+)?/gi, "private context") .replace(/\b(raw trust score|trust score|wallet|hotkey|coldkey|seed phrase|mnemonic)\b/gi, "private context") - .replace(/\b(public score estimate|estimated score|score estimate|reward estimates?|payout|farming|scoreability|score preview|projected score changes?)\b/gi, "private context") + .replace(/\b(public score estimate|estimated score|score estimate|estimated rewards?|rewards?|reward estimates?|payout|farming|scoreability|score preview|projected score changes?)\b/gi, "private context") .replace(/\b(private reviewability|reviewability internals?)\b/gi, "private context") - .replace(/\b(private ranking|private rankings)\b/gi, "private context") + .replace(/\b(private rankings?|rankings?)\b/gi, "private context") .replace(/\b(?:open_pr_pressure|closed_pr_credibility|low_credibility|maintainer_lane|inactive_or_unknown_lane|issue_discovery_only)\b/gi, "private context") .replace(/\b(?:credibility(?: updates?)?|closed pr credibility|low credibility|open pr pressure)\b/gi, "private context") .replace(/\blikely_duplicate\b/gi, "possible overlap with existing work"); diff --git a/src/scenarios/input-model.ts b/src/scenarios/input-model.ts index f7a08d9973..3087639d5b 100644 --- a/src/scenarios/input-model.ts +++ b/src/scenarios/input-model.ts @@ -30,7 +30,7 @@ export const scenarioSignalSources = [ export type ScenarioSignalSource = (typeof scenarioSignalSources)[number]; const FORBIDDEN_PUBLIC_LANGUAGE = - /wallet|hotkey|coldkey|mnemonic|seed phrase|payout|reward[-\s]?estimate|farming|raw trust|trust[-\s]?score|scoreability|private[-\s]?reviewability|public[-\s]?score[-\s]?(?:estimate|prediction)/i; + /wallet|hotkey|coldkey|mnemonic|seed phrase|payout|estimated[-\s]?rewards?|rewards?|reward[-\s]?estimate|rankings?|farming|raw trust|trust[-\s]?score|scoreability|private[-\s]?reviewability|public[-\s]?score[-\s]?(?:estimate|prediction)/i; const FORBIDDEN_SOURCE_UPLOAD_KEYS = /^(?:sourceContent|sourceContents|fileContent|fileContents|rawSource|rawSourceContent|content|contents|diff|patch|rawDiff)$/i; diff --git a/test/unit/scenario-input-model.test.ts b/test/unit/scenario-input-model.test.ts index 28e17d8161..e19983e018 100644 --- a/test/unit/scenario-input-model.test.ts +++ b/test/unit/scenario-input-model.test.ts @@ -15,7 +15,7 @@ import { } from "../../src/scenarios/input-model"; const FORBIDDEN_PUBLIC_LANGUAGE = - /wallet|hotkey|coldkey|mnemonic|seed phrase|payout|reward[-\s]?estimate|farming|raw trust|trust[-\s]?score|scoreability|private[-\s]?reviewability|public[-\s]?score[-\s]?(?:estimate|prediction)/i; + /wallet|hotkey|coldkey|mnemonic|seed phrase|payout|estimated[-\s]?rewards?|rewards?|reward[-\s]?estimate|rankings?|farming|raw trust|trust[-\s]?score|scoreability|private[-\s]?reviewability|public[-\s]?score[-\s]?(?:estimate|prediction)/i; function completeInput() { return buildScenarioInput({ @@ -149,6 +149,26 @@ describe("public vs private serialization", () => { expect(privateSnapshot.assumptions[0]?.detail).toMatch(/trust score/i); }); + it("sanitizes reward and ranking language in estimate snapshots", () => { + const input = buildScenarioInput({ + scenarioType: "general_repo", + repoFullName: "octo/demo", + estimates: [ + createScenarioSignalEntry({ + id: "sensitive_estimate", + kind: "estimate", + label: "Estimated rewards ranking", + detail: "estimated rewards place this contributor in the top ranking and rankings table", + source: "gittensory_projection", + }), + ], + }); + + const serialized = JSON.stringify(serializeScenarioInputPublic(input)); + expect(serialized).not.toMatch(FORBIDDEN_PUBLIC_LANGUAGE); + expect(serialized).toMatch(/private context/i); + }); + it("omits optional state sections when not provided", () => { const snapshot = serializeScenarioInputPublic( buildScenarioInput({ @@ -302,6 +322,10 @@ describe("invariants", () => { "public score estimate", "private reviewability", "farming loop", + "estimated rewards", + "rewards", + "ranking", + "rankings", ]; for (const sample of samples) { const input = buildScenarioInput({