Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/signals/extension-contributor-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,14 @@ export function contributorReadinessBand(total: number): ContributorReadinessBan
// The upstream builders are already contributor-facing, but every string is re-checked here and any
// forbidden private term (reward/wallet/key material/raw trust score/etc.) is redacted rather than
// leaked. Kept local (no import) so this module stays cycle-free and the API never 500s on a stray term.
// The bare `cohort`/`ranking`/`miner-originated`/`human-originated`/`reviewability` alternatives (and the
// `[-_\s]?` separator on the originated pair) mirror src/signals/redaction.ts's canonical PUBLIC_UNSAFE_TERMS
// so this overlay stops leaking economic-identity terms that surface drifted away from (#5840). The compound
// `reviewability` terms stay ordered before the bare word so "reviewability internals"/"private reviewability"
// still match as a whole. Kept hand-synced (no import) so this module stays cycle-free; a drift-guard test
// (extension-contributor-context.test.ts) fails if these diverge from PUBLIC_UNSAFE_TERMS again.
const FORBIDDEN_EXTENSION_TERMS =
/\b(?:rewards?|payouts?|farming|wallets?|hotkeys?|coldkeys?|seed[-\s]?phrases?|mnemonics?|private[-\s]?keys?|raw[-\s]?trust(?:[-\s]?scores?)?|trust[-\s]?scores?|score[-\s]?(?:estimate|preview|prediction)s?|estimated[-\s]?scores?|scoreability|private[-\s]?reviewability|reviewability[-\s]?internals?|private[-\s]?rankings?)\b/gi;
/\b(?:rewards?|payouts?|farming|wallets?|hotkeys?|coldkeys?|seed[-\s]?phrases?|mnemonics?|private[-\s]?keys?|raw[-\s]?trust(?:[-\s]?scores?)?|trust[-\s]?scores?|score[-\s]?(?:estimate|preview|prediction)s?|estimated[-\s]?scores?|scoreability|cohort\w*|ranking\w*|miner[-_\s]?originated|human[-_\s]?originated|private[-\s]?reviewability|reviewability[-\s]?internals?|reviewability|private[-\s]?rankings?)\b/gi;

export function redactExtensionText(text: string): string {
return text.replace(FORBIDDEN_EXTENSION_TERMS, "[redacted]").replace(/\s+/g, " ").trim();
Expand Down
62 changes: 61 additions & 1 deletion test/unit/extension-contributor-context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import {
redactExtensionText,
} from "../../src/signals/extension-contributor-context";
import type { ContributorOpportunity, PublicReadinessScore } from "../../src/signals/engine";
import { PUBLIC_UNSAFE_TERMS } from "../../src/signals/redaction";

const FORBIDDEN_PUBLIC_TERMS = /wallet|hotkey|coldkey|mnemonic|reward|payout|farming|raw trust|trust score|scoreability|reviewability internals|private ranking/i;
const FORBIDDEN_PUBLIC_TERMS =
/wallet|hotkey|coldkey|mnemonic|reward|payout|farming|raw trust|trust score|scoreability|cohort|ranking|miner-originated|human-originated|reviewability/i;

function opportunity(over: Partial<ContributorOpportunity> = {}): ContributorOpportunity {
return {
Expand Down Expand Up @@ -59,6 +61,64 @@ describe("redactExtensionText", () => {
it("leaves safe text untouched", () => {
expect(redactExtensionText("Maintainer-created issue, good fit.")).toBe("Maintainer-created issue, good fit.");
});

// #5840: FORBIDDEN_EXTENSION_TERMS had drifted from redaction.ts's canonical PUBLIC_UNSAFE_TERMS and let
// these economic-identity terms through unredacted.
it("redacts bare cohort, previously leaked", () => {
expect(redactExtensionText("Cohort diagnostics flagged this PR")).toBe("[redacted] diagnostics flagged this PR");
});

it("redacts bare ranking, previously leaked", () => {
expect(redactExtensionText("Your ranking dropped this week")).toBe("Your [redacted] dropped this week");
});

it("redacts miner-originated / human-originated, previously leaked", () => {
expect(redactExtensionText("This looks miner-originated, not human-originated")).toBe("This looks [redacted], not [redacted]");
// the [-_\s]? separator matches underscore and space forms too, matching PUBLIC_UNSAFE_TERMS.
expect(redactExtensionText("miner_originated and human originated")).toBe("[redacted] and [redacted]");
});

it("redacts standalone reviewability while still redacting the compound forms as a whole", () => {
expect(redactExtensionText("Reviewability is limited right now")).toBe("[redacted] is limited right now");
expect(redactExtensionText("reviewability internals exposed")).toBe("[redacted] exposed");
expect(redactExtensionText("private reviewability data")).toBe("[redacted] data");
});

it("stays in sync with the canonical PUBLIC_UNSAFE_TERMS vocabulary (drift guard)", () => {
// Every economic-identity term the canonical boundary blocks must also be scrubbed by this overlay, so the
// two vocabularies can't silently diverge again. Bare `score` is the one intentional exception: this
// surface returns readiness as public BANDS, and redaction.ts's own note records sibling surfaces that
// deliberately do not redact a bare `score`.
const canonical = new RegExp(PUBLIC_UNSAFE_TERMS, "i");
const intentionalExceptions = new Set(["public score"]);
const samples = [
"reward",
"wallet",
"hotkey",
"coldkey",
"mnemonic",
"payout",
"farming",
"raw trust",
"trust score",
"ranking",
"cohort",
"miner-originated",
"human-originated",
"private reviewability",
"reviewability",
"public score", // intentional exception -- canonical matches it, this overlay deliberately does not
];
for (const sample of samples) {
expect(canonical.test(sample)).toBe(true); // sanity: the canonical vocabulary really does flag it
const redacted = redactExtensionText(sample);
if (intentionalExceptions.has(sample)) {
expect(redacted).toBe(sample); // band-gated: intentionally left as-is
} else {
expect(redacted).toBe("[redacted]");
}
}
});
});

describe("buildExtensionIssueFit", () => {
Expand Down