fix(core,dashboard): review fixes — reject date/UUID dedup anchors; gate provenance issueUrl links to http(s)
Code-review findings on 085f7b99c: a bare date like 2026-07-22 qualified
as a distinctive slug, so unrelated failure reports quoting the same date
silently converged (and the date outranked a real file-path anchor in the
sorted-first pick); reject slugs whose segments are all hex/numeric.
Also render non-http(s) sourceMetadata.issueUrl values as plain text to
block javascript:-scheme links from API-supplied metadata.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -180,6 +180,24 @@ describe("findSameAgentDuplicates", () => {
|
||||
expect(first).toBe(second);
|
||||
});
|
||||
|
||||
it("never anchors on dates or UUIDs, and a date never outranks a file-path anchor", () => {
|
||||
expect(computeCrossParentDiagnosticClaimId({
|
||||
description: "Fix nightly build failure observed on 2026-07-22 in the settings modal",
|
||||
})).toBeNull();
|
||||
expect(computeCrossParentDiagnosticClaimId({
|
||||
description: "Fix failed run d3be2cd6-9221-4e1d-a27a-c5fbe04f9200 stuck in merge",
|
||||
})).toBeNull();
|
||||
|
||||
const withDate = computeCrossParentDiagnosticClaimId({
|
||||
description: "Fix typecheck error in packages/core/src/store.ts since 2026-07-22",
|
||||
});
|
||||
const withoutDate = computeCrossParentDiagnosticClaimId({
|
||||
description: "Fix typecheck error in packages/core/src/store.ts",
|
||||
});
|
||||
expect(withDate).not.toBeNull();
|
||||
expect(withDate).toBe(withoutDate);
|
||||
});
|
||||
|
||||
it("does not claim ordinary work that merely names a file path", () => {
|
||||
expect(computeCrossParentDiagnosticClaimId({
|
||||
description: "Add caching to packages/core/src/store.ts for faster board loads",
|
||||
|
||||
@@ -121,8 +121,16 @@ function normalizeDiagnosticObject(value: string): string | null {
|
||||
return /[0-9@./_-]/.test(normalized) ? normalized : null;
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:TaskCreationDeduplication 2026-07-22-15:20:
|
||||
A distinctive slug must have at least one non-hex segment. Dates ("2026-07-22") and
|
||||
UUIDs are hyphen-shaped but identify WHEN/WHICH RUN, not WHAT is broken: two unrelated
|
||||
failure reports quoting the same date must not silently converge into one task, and a
|
||||
date must never outrank a real file-path anchor in the sorted-first object pick.
|
||||
*/
|
||||
function isDistinctiveSlug(value: string): boolean {
|
||||
return /^[a-z0-9]+(?:-[a-z0-9]+){2,}$/.test(value);
|
||||
return /^[a-z0-9]+(?:-[a-z0-9]+){2,}$/.test(value)
|
||||
&& value.split("-").some((segment) => !/^[0-9a-f]+$/.test(segment));
|
||||
}
|
||||
|
||||
function normalizeDiagnosticPath(value: string): string {
|
||||
|
||||
@@ -306,13 +306,23 @@ export function TaskTokenStatsPanel({ tokenUsage, loading, task }: TaskTokenStat
|
||||
<dd>{task.sourceAgentId}</dd>
|
||||
</div>
|
||||
) : null}
|
||||
{/*
|
||||
FNXC:TaskStatsProvenance 2026-07-22-15:20:
|
||||
sourceMetadata is writable through the task-creation API, so issueUrl is untrusted:
|
||||
only http(s) URLs may render as a link (blocks javascript:-scheme injection);
|
||||
anything else renders as plain text.
|
||||
*/}
|
||||
{typeof task.sourceMetadata?.issueUrl === "string" ? (
|
||||
<div className="task-token-stats-panel__detail-row">
|
||||
<dt>{t("taskDetail.provenance.importedFrom", "Imported from")}</dt>
|
||||
<dd>
|
||||
<a href={task.sourceMetadata.issueUrl} target="_blank" rel="noreferrer">
|
||||
{task.sourceMetadata.issueUrl}
|
||||
</a>
|
||||
{/^https?:\/\//i.test(task.sourceMetadata.issueUrl) ? (
|
||||
<a href={task.sourceMetadata.issueUrl} target="_blank" rel="noreferrer">
|
||||
{task.sourceMetadata.issueUrl}
|
||||
</a>
|
||||
) : (
|
||||
task.sourceMetadata.issueUrl
|
||||
)}
|
||||
</dd>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
@@ -311,6 +311,16 @@ describe("TaskTokenStatsPanel", () => {
|
||||
.toHaveAttribute("href", "https://github.com/Runfusion/Fusion/issues/2356");
|
||||
});
|
||||
|
||||
it("renders a non-http issueUrl as plain text, never as a link", () => {
|
||||
render(<TaskTokenStatsPanel loading={false} tokenUsage={undefined} task={makeTask({
|
||||
sourceType: "api",
|
||||
sourceMetadata: { issueUrl: "javascript:alert(1)" },
|
||||
})} />);
|
||||
|
||||
expect(screen.getByText("javascript:alert(1)")).toBeInTheDocument();
|
||||
expect(screen.queryByRole("link", { name: "javascript:alert(1)" })).toBeNull();
|
||||
});
|
||||
|
||||
it("omits the provenance section when the task has no recorded source", () => {
|
||||
render(<TaskTokenStatsPanel loading={false} tokenUsage={undefined} task={makeTask()} />);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user