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
13 changes: 7 additions & 6 deletions packages/loopover-engine/src/track-record-summary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -433,18 +433,19 @@ export function renderTrackRecordSummaryMarkdown(summary: TrackRecordSummary): s
if (summary.outcomes.openIgnored > 0) {
bodyLines.push(`- Open PRs ignored for rate: ${summary.outcomes.openIgnored}`);
}

// #6772 / #7444: fail-closed on COMPUTED fields only. Caller/GitHub-sourced identity-like free text must not
// trip the blocklist: the GitHub login is appended after this scan, and public evidence URLs are structural
// links (repo/path segments can legitimately contain hyphen- or slash-bounded blocklisted substrings such as
// `wallet-connect`) — scanning them was the same false-positive crash class #6772 fixed for login.
assertPublicSummaryText(bodyLines.join("\n"));

if (summary.incidents.hasPublicIncident && summary.incidents.evidenceUrls.length > 0) {
bodyLines.push(
`- Public evidence: ${summary.incidents.evidenceUrls.map((url) => markdownSafe(url)).join(", ")}`,
);
}

// #6772: fail-closed on the COMPUTED fields only -- a blocklisted term there would be a genuine leak. The
// GitHub login is caller-provided identity (already markdown-escaped below), not computed private data, so a
// legitimate username that merely contains a blocklisted word bounded by hyphens (e.g. "team-wallet") must
// NOT crash rendering. Scanning the whole block including the identity line was the bug.
assertPublicSummaryText(bodyLines.join("\n"));

const lines = [
"### Public contributor record",
"",
Expand Down
29 changes: 29 additions & 0 deletions packages/loopover-engine/test/track-record-summary.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,35 @@ test("renderTrackRecordSummaryMarkdown fails closed if a blocked public field is
);
});

test("renderTrackRecordSummaryMarkdown tolerates a login containing a blocklisted substring (#6772 / #7444)", () => {
const summary = computeTrackRecordSummary({
login: "team-wallet",
now: NOW,
config: { includeTrackRecordSummary: true, warnings: [] },
outcomes: [],
});
const markdown = renderTrackRecordSummaryMarkdown(summary);
assert.match(markdown, /GitHub login: team-wallet/u);
});

test("renderTrackRecordSummaryMarkdown tolerates evidence URLs containing a blocklisted path substring (#7444)", () => {
const summary = computeTrackRecordSummary({
login: "miner",
now: NOW,
config: { includeTrackRecordSummary: true, warnings: [] },
outcomes: [],
incidents: [
{
login: "miner",
kind: "ban",
publicEvidenceUrl: "https://example.test/org/wallet-connect/issues/1",
},
],
});
const markdown = renderTrackRecordSummaryMarkdown(summary);
assert.match(markdown, /Public evidence: https:\/\/example\.test\/org\/wallet-connect\/issues\/1/u);
});

test("computeTrackRecordSummary is byte-stable for equivalent repeated calls", () => {
const input = {
login: "miner",
Expand Down
14 changes: 14 additions & 0 deletions test/unit/track-record-summary-login-blocklist.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,18 @@ describe("renderTrackRecordSummaryMarkdown login vs public-field blocklist (#677
expect(md).toContain("- Open PRs ignored for rate: 3");
expect(md).toMatch(/- Public evidence: .*example\.test\/record/u);
});

it("REGRESSION (#7444): renders an evidence URL whose path contains a blocklisted substring instead of throwing", () => {
const base = computeTrackRecordSummary({ login: "miner", now: NOW, config, outcomes: [] });
const md = renderTrackRecordSummaryMarkdown({
...base,
incidents: {
...base.incidents,
hasPublicIncident: true,
label: "public conduct incident present",
evidenceUrls: ["https://example.test/org/wallet-connect/issues/1"],
},
});
expect(md).toContain("https://example.test/org/wallet-connect/issues/1");
});
});