From 75cfd87c320f41110374e4d6060b6d4446ff4ef3 Mon Sep 17 00:00:00 2001 From: RealDiligent Date: Sun, 26 Jul 2026 21:26:28 +0800 Subject: [PATCH] fix(services): catch the plural "rankings" in remediation-plan's forbidden-word gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FORBIDDEN_PATTERN matched only the bare singular `ranking` — the trailing `\b` can never sit between "g" and "s", so "rankings" slipped past this secondary public-safety gate. Change `ranking` to `rankings?`, matching the sibling regex fix in input-model.ts (#913), and export the pattern so its plural coverage can be asserted directly (the term is unreachable through the public builder because sanitizePublicComment rewrites it before this gate is consulted). Closes #8886 Co-Authored-By: Claude Opus 4.8 --- src/services/remediation-plan.ts | 7 +++++-- test/unit/remediation-plan.test.ts | 11 ++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/services/remediation-plan.ts b/src/services/remediation-plan.ts index f5246b403c..f8d14d4333 100644 --- a/src/services/remediation-plan.ts +++ b/src/services/remediation-plan.ts @@ -34,8 +34,11 @@ export type RemediationPlanInput = { }>; }; -const FORBIDDEN_PATTERN = - /\b(reward\w*|wallet|hotkey|coldkey|mnemonic|farming|payout|ranking|raw[-_\s]?trust|trust[-_\s]?score|private[-_\s]?reviewability|reviewability|private[-_\s]?scoreability|scoreability|score\w*|token[-_\s]?gate|token[-_\s]?score|base[-_\s]?score|multiplier|eligibility)\b|\/Users\/|\/home\/|\/tmp\/|[A-Z]:[\\/]Users[\\/]/i; +// #8886: exported so its forbidden-term coverage (specifically the plural `rankings`, unreachable through the +// public builder because sanitizePublicComment rewrites the term before this secondary gate ever sees it) can +// be asserted directly, matching how the issue itself verified the gap. +export const FORBIDDEN_PATTERN = + /\b(reward\w*|wallet|hotkey|coldkey|mnemonic|farming|payout|rankings?|raw[-_\s]?trust|trust[-_\s]?score|private[-_\s]?reviewability|reviewability|private[-_\s]?scoreability|scoreability|score\w*|token[-_\s]?gate|token[-_\s]?score|base[-_\s]?score|multiplier|eligibility)\b|\/Users\/|\/home\/|\/tmp\/|[A-Z]:[\\/]Users[\\/]/i; const SOURCE_PRIORITY: Record = { account_state: 0, diff --git a/test/unit/remediation-plan.test.ts b/test/unit/remediation-plan.test.ts index 60a253301b..df22a0ffa5 100644 --- a/test/unit/remediation-plan.test.ts +++ b/test/unit/remediation-plan.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { buildRemediationPlan } from "../../src/services/remediation-plan"; +import { buildRemediationPlan, FORBIDDEN_PATTERN } from "../../src/services/remediation-plan"; const FORBIDDEN = /\b(wallet|hotkey|coldkey|mnemonic|farming|payout|raw[-_\s]?trust|score\w*|scoreability|token[-_\s]?score|base[-_\s]?score)\b/i; @@ -188,6 +188,15 @@ describe("buildRemediationPlan", () => { ]); }); + it("FORBIDDEN_PATTERN catches both the singular 'ranking' and the plural 'rankings' (#8886)", () => { + // The bare singular `ranking` could never match inside "rankings" (no word boundary between "g" and "s"), + // so the plural leaked past this secondary gate; `rankings?` fixes it while still matching the singular. + expect(FORBIDDEN_PATTERN.test("this shows the ranking of contributors")).toBe(true); + expect(FORBIDDEN_PATTERN.test("this shows the rankings of contributors")).toBe(true); + // A benign word that merely starts the same must still pass through untouched. + expect(FORBIDDEN_PATTERN.test("the rankboard is empty")).toBe(false); + }); + it("falls back to public-safe copy when every blocker string is fully redacted", () => { const plan = buildRemediationPlan({ login: "miner",