Summary
sanitizePublicText in src/services/miner-dashboard-recommendations.ts (new in #423) is the public-safety boundary for the miner-dashboard change labels and rerun reasons. It strips local filesystem paths and a list of sensitive words (wallets, hotkeys, trust scores, rewards, scoreability, …) but has no rule for secret tokens (ghp_, github_pat_, gts_, glpat-, sk-). The output is served on /v1/app/miner-dashboard.
Evidence
// src/services/miner-dashboard-recommendations.ts:383
function sanitizePublicText(value: string): string {
return value
.replace(LOCAL_PATH, "[local path]")
.replace(FORBIDDEN_PUBLIC_TEXT, "private context")
.replace(/s+/g, " ")
.trim();
}
FORBIDDEN_PUBLIC_TEXT (line 43) and LOCAL_PATH (line 45) contain no token patterns. Yet the module's own test declares tokens forbidden in this output:
// test/unit/miner-dashboard-recommendations.test.ts:11
const FORBIDDEN_PUBLIC_CHANGE_TEXT = /…|github_pat|ghp_/i;
// line 83: expect(...).not.toMatch(FORBIDDEN_PUBLIC_CHANGE_TEXT)
The assertion passes only because no fixture injects a token — no code path actually removes one.
Inconsistent with the established pattern
Every other public/role-facing sanitizer redacts tokens:
src/services/agent-action-explanation-card.ts:11 — github_pat_…|gh[pousr]_… + local paths
src/services/weekly-value-report.ts:413 — .replace(/�(?:ghp_|github_pat_|gts_|glpat-|sk-)[A-Za-z0-9_=-]{8,}/g, "<redacted-token>")
src/services/control-panel-roles.ts:257 — identical token rule
miner-dashboard-recommendations.ts is the outlier.
Impact
The change/rerun metadata surfaces free-text decision-pack fields (actionPortfolio.topActions[].rerunWhen, scoreBlocker codes, recommendation, roleContext, manifest summaries). A token landing in any of those is emitted verbatim on /v1/app/miner-dashboard — the defense-in-depth case the sibling services already guard against.
Suggested fix
const FORBIDDEN_TOKEN = /�(?:ghp_|github_pat_|gts_|glpat-|sk-)[A-Za-z0-9_=-]{8,}/g;
function sanitizePublicText(value: string): string {
return value
.replace(LOCAL_PATH, "[local path]")
.replace(FORBIDDEN_TOKEN, "private context")
.replace(FORBIDDEN_PUBLIC_TEXT, "private context")
.replace(/s+/g, " ")
.trim();
}
Add a fixture that injects a ghp_… token into a surfaced field (e.g. rerunWhen) so the existing assertion actually exercises token redaction.
Summary
sanitizePublicTextinsrc/services/miner-dashboard-recommendations.ts(new in #423) is the public-safety boundary for the miner-dashboard change labels and rerun reasons. It strips local filesystem paths and a list of sensitive words (wallets, hotkeys, trust scores, rewards, scoreability, …) but has no rule for secret tokens (ghp_,github_pat_,gts_,glpat-,sk-). The output is served on/v1/app/miner-dashboard.Evidence
FORBIDDEN_PUBLIC_TEXT(line 43) andLOCAL_PATH(line 45) contain no token patterns. Yet the module's own test declares tokens forbidden in this output:The assertion passes only because no fixture injects a token — no code path actually removes one.
Inconsistent with the established pattern
Every other public/role-facing sanitizer redacts tokens:
src/services/agent-action-explanation-card.ts:11—github_pat_…|gh[pousr]_…+ local pathssrc/services/weekly-value-report.ts:413—.replace(/�(?:ghp_|github_pat_|gts_|glpat-|sk-)[A-Za-z0-9_=-]{8,}/g, "<redacted-token>")src/services/control-panel-roles.ts:257— identical token ruleminer-dashboard-recommendations.tsis the outlier.Impact
The change/rerun metadata surfaces free-text decision-pack fields (
actionPortfolio.topActions[].rerunWhen,scoreBlockercodes,recommendation,roleContext, manifest summaries). A token landing in any of those is emitted verbatim on/v1/app/miner-dashboard— the defense-in-depth case the sibling services already guard against.Suggested fix
Add a fixture that injects a
ghp_…token into a surfaced field (e.g.rerunWhen) so the existing assertion actually exercises token redaction.