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
1 change: 1 addition & 0 deletions public/assets/Renovate.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import CVERainbowBadge from "../../../../../../components/CVERainbowBadge";
import Page from "../../../../../../components/Page";
import { RiskHistoryDistributionDiagram } from "../../../../../../components/RiskHistoryDistributionDiagram";
import SeverityCard from "../../../../../../components/SeverityCard";
import QuickfixNotification from "../../../../../../components/QuickfixNotification";
import { AsyncButton, Button } from "../../../../../../components/ui/button";
import {
Card,
Expand Down Expand Up @@ -52,12 +53,14 @@ import {
normalizeContentTree,
reduceRiskHistories,
sortRisk,
withMockFixableRiskHistory,
} from "../../../../../../utils/view";
import useRouterQuery from "../../../../../../hooks/useRouterQuery";
import Link from "next/link";
import { toast } from "sonner";
import { useSession } from "@/context/SessionContext";
import { browserApiClient } from "../../../../../../services/devGuardApi";
import Callout from "@/components/common/Callout";
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Callout is imported but never used in this file, which will trigger unused-import linting. Remove the import or use the component.

Suggested change
import Callout from "@/components/common/Callout";

Copilot uses AI. Check for mistakes.

const OverviewPage = () => {
const search = useSearchParams();
Expand Down Expand Up @@ -178,7 +181,10 @@ const OverviewPage = () => {
);

const completeRiskHistory: RiskHistory[][] = useMemo(() => {
const groups = groupBy(riskHistory ?? [], "day");
const groups = groupBy(
withMockFixableRiskHistory(riskHistory ?? []),
"day",
);
Comment on lines 183 to +187
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

withMockFixableRiskHistory(...) injects fabricated totals/fixable counts (see its implementation in utils/view). Using this in the overview page means the UI will display incorrect numbers in non-dev environments. This should be removed or gated (e.g. behind a dev/feature flag) and replaced with real API-provided fixable metrics.

Copilot uses AI. Check for mistakes.
const days = Object.keys(groups).sort();
return days.map((day) => {
return groups[day];
Expand Down Expand Up @@ -252,6 +258,59 @@ const OverviewPage = () => {
);
}, [completeRiskHistory, mode]);

const criticalFixableAmount = useMemo(() => {
if (completeRiskHistory.length === 0) return 0;
return completeRiskHistory[completeRiskHistory.length - 1].reduce(
(sum, r) => sum + (r?.cvePurlFixableCritical ?? 0),
0,
);
}, [completeRiskHistory]);

const highFixableAmount = useMemo(() => {
if (completeRiskHistory.length === 0) return 0;
return completeRiskHistory[completeRiskHistory.length - 1].reduce(
(sum, r) => sum + (r?.cvePurlFixableHigh ?? 0),
0,
);
}, [completeRiskHistory]);

const mediumFixableAmount = useMemo(() => {
if (completeRiskHistory.length === 0) return 0;
return completeRiskHistory[completeRiskHistory.length - 1].reduce(
(sum, r) => sum + (r?.cvePurlFixableMedium ?? 0),
0,
);
}, [completeRiskHistory]);

const lowFixableAmount = useMemo(() => {
if (completeRiskHistory.length === 0) return 0;
return completeRiskHistory[completeRiskHistory.length - 1].reduce(
(sum, r) => sum + (r?.cvePurlFixableLow ?? 0),
0,
);
}, [completeRiskHistory]);

const totalAmount = useMemo(() => {
if (completeRiskHistory.length === 0) return 0;
return completeRiskHistory[completeRiskHistory.length - 1].reduce(
(sum, r) =>
sum +
(r?.totalAmount ??
(r?.cvePurlLow ?? 0) +
(r?.cvePurlMedium ?? 0) +
(r?.cvePurlHigh ?? 0) +
(r?.cvePurlCritical ?? 0)),
0,
);
}, [completeRiskHistory]);
const quickfixAmount =
criticalFixableAmount +
highFixableAmount +
mediumFixableAmount +
lowFixableAmount;

console.log(quickfixAmount);

Comment on lines +312 to +313
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leftover debug logging: console.log(quickfixAmount) will spam the browser console for every render. Remove this (or replace with proper instrumentation if needed).

Suggested change
console.log(quickfixAmount);

Copilot uses AI. Check for mistakes.
const vulnerableArtifacts = useMemo(() => {
if (completeRiskHistory.length === 0) return [];
const latestRiskHistory =
Expand Down Expand Up @@ -357,6 +416,7 @@ const OverviewPage = () => {
isLoading={riskHistoryLoading}
variant="critical"
currentAmount={criticalAmount}
fixableAmount={criticalFixableAmount}
queryIntervalStart={8.9}
queryIntervalEnd={10}
mode={mode}
Expand All @@ -365,6 +425,7 @@ const OverviewPage = () => {
isLoading={riskHistoryLoading}
variant="high"
currentAmount={highAmount}
fixableAmount={highFixableAmount}
queryIntervalStart={6.9}
queryIntervalEnd={9}
mode={mode}
Expand All @@ -373,6 +434,7 @@ const OverviewPage = () => {
isLoading={riskHistoryLoading}
variant="medium"
currentAmount={mediumAmount}
fixableAmount={mediumFixableAmount}
queryIntervalStart={3.9}
queryIntervalEnd={7}
mode={mode}
Expand All @@ -381,11 +443,21 @@ const OverviewPage = () => {
isLoading={riskHistoryLoading}
variant="low"
currentAmount={lowAmount}
fixableAmount={lowFixableAmount}
queryIntervalStart={0}
queryIntervalEnd={4}
mode={mode}
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="col-2">
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

className="col-2" doesn't correspond to a standard Tailwind grid utility (likely intended col-span-2). As written, this wrapper probably won't span both columns and may break the layout.

Suggested change
<div className="col-2">
<div className="col-span-2">

Copilot uses AI. Check for mistakes.
<QuickfixNotification
totalAmount={totalAmount}
fixableAmount={quickfixAmount}
isLoading={riskHistoryLoading}
/>
</div>
</div>
<div className="grid grid-cols-4 gap-4">
<div className="grid grid-cols-2 col-span-2 gap-4">
<AverageFixingTimeChart
Expand Down
70 changes: 70 additions & 0 deletions src/components/QuickfixNotification.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { FunctionComponent } from "react";
import { ArrowRightIcon, FileTextIcon, WrenchIcon } from "lucide-react";
import { Button } from "./ui/button";
import { Card, CardContent } from "./ui/card";
import { Progress } from "./ui/progress";
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import of Progress is unused. With the repo's ESLint setup (next/core-web-vitals), this will be reported as an unused import; remove it or actually use the component.

Suggested change
import { Progress } from "./ui/progress";

Copilot uses AI. Check for mistakes.
import Image from "next/image";

interface Props {
totalAmount: number;
fixableAmount: number;
isLoading: boolean;
}

const QuickfixNotification: FunctionComponent<Props> = ({
totalAmount,
fixableAmount,
isLoading = false,
}) => {
if (isLoading || fixableAmount <= 0 || totalAmount <= 0) {
return null;
}
const fixableCount = Math.min(fixableAmount, totalAmount);
const manualReportCount = Math.max(0, totalAmount - fixableCount);

return (
<Card className="w-full shadow-sm">
<CardContent className="space-y-4 p-4">
<div className="rounded-xl border p-4">
<div className="grid grid-cols-2 gap-4">
<div className="space-y-1 border-r border-border/60 pr-4">
<div className="flex items-center gap-2 text-3xl font-semibold text-yellow-500">
<WrenchIcon className="h-5 w-5" />
<span>{fixableCount}</span>
</div>
<div className="text-sm text-muted-foreground">Quick-fixable</div>
</div>

<div className="space-y-1 pl-4">
<div className="flex items-center gap-2 text-3xl font-semibold text-muted-foreground">
<FileTextIcon className="h-5 w-5" />
<span>{manualReportCount}</span>
</div>
<div className="text-sm text-muted-foreground">Manual report</div>
</div>
</div>
</div>

<div className="flex items-center justify-between gap-3">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<span className="font-medium text-foreground">{totalAmount}</span>
<span>total vulnerabilities</span>
</div>

<Button variant="default" className="gap-2">
<Image
src="/assets/renovate.svg"
alt="Renovate icon"
width={16}
height={16}
/>
Comment on lines +54 to +60
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Renovate icon path casing doesn't match the file added in this PR (public/assets/Renovate.svg). On case-sensitive filesystems this will 404. Either rename the asset to public/assets/renovate.svg or update src to /assets/Renovate.svg to match exactly.

Copilot uses AI. Check for mistakes.
Create PR
<ArrowRightIcon className="h-4 w-4" />
</Button>
</div>
</CardContent>
</Card>
);
};

export default QuickfixNotification;
11 changes: 10 additions & 1 deletion src/components/SeverityCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ import {
} from "./ui/card";
import { Skeleton } from "./ui/skeleton";
import { useSearchParams } from "next/navigation";
import { WrenchIcon } from "lucide-react";

interface Props {
currentAmount: number;
fixableAmount: number;
queryIntervalStart: number;
queryIntervalEnd: number;
variant: "high" | "medium" | "low" | "critical";
Expand All @@ -27,6 +29,7 @@ interface Props {

const SeverityCard: FunctionComponent<Props> = ({
currentAmount,
fixableAmount,
variant,
queryIntervalStart,
queryIntervalEnd,
Expand Down Expand Up @@ -77,7 +80,13 @@ const SeverityCard: FunctionComponent<Props> = ({
);
}
return (
<Card>
<Card className="relative">
{fixableAmount > 0 && (
<div className="absolute right-3 top-3 z-10 flex items-center gap-1 rounded-full border px-2 py-1 text-xs font-medium text-yellow-500 shadow-sm">
<WrenchIcon className="h-3.5 w-3.5" />
<span>{fixableAmount}</span>
</div>
)}
<CardHeader className="pb-2">
<CardTitle className="flex flex-row items-start justify-between">
<span>
Expand Down
12 changes: 12 additions & 0 deletions src/types/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,18 @@ export interface RiskHistory {
cvePurlMediumCvss: number;
cvePurlHighCvss: number;
cvePurlCriticalCvss: number;

totalAmount?: number;

fixableLow: number;
fixableMedium: number;
fixableHigh: number;
fixableCritical: number;

cvePurlFixableLow: number;
cvePurlFixableMedium: number;
cvePurlFixableHigh: number;
cvePurlFixableCritical: number;
Comment on lines +811 to +819
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These new RiskHistory properties (fixable*, cvePurlFixable*, totalAmount) are being treated as nullable/derived elsewhere (lots of ?? 0 and even mock filling). Making them required here means all RiskHistory objects in the codebase are now expected to contain them (note RiskHistory is declared twice in this file and the interfaces merge). If the backend doesn't always send these fields yet, mark them optional (or split into a separate extended type) to avoid incorrect type guarantees and forced as RiskHistory casts.

Suggested change
fixableLow: number;
fixableMedium: number;
fixableHigh: number;
fixableCritical: number;
cvePurlFixableLow: number;
cvePurlFixableMedium: number;
cvePurlFixableHigh: number;
cvePurlFixableCritical: number;
fixableLow?: number;
fixableMedium?: number;
fixableHigh?: number;
fixableCritical?: number;
cvePurlFixableLow?: number;
cvePurlFixableMedium?: number;
cvePurlFixableHigh?: number;
cvePurlFixableCritical?: number;

Copilot uses AI. Check for mistakes.
}

export type ReleaseRiskHistory = Omit<
Expand Down
12 changes: 12 additions & 0 deletions src/utils/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,18 @@ export function padRiskHistory<T extends RiskHistoryEl>(
cvePurlHighCvss: 0,
cvePurlMediumCvss: 0,
cvePurlLowCvss: 0,

totalAmount: 0,

fixableLow: 0,
fixableMedium: 0,
fixableHigh: 0,
fixableCritical: 0,

cvePurlFixableLow: 0,
cvePurlFixableMedium: 0,
cvePurlFixableHigh: 0,
cvePurlFixableCritical: 0,
},
...r.riskHistory,
];
Expand Down
36 changes: 36 additions & 0 deletions src/utils/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,32 @@ export const generateColor = (str: string) => {
return colors[hash % colors.length];
};

export const withMockFixableRiskHistory = (
data: RiskHistory[],
): RiskHistory[] => {
return data.map((entry) => ({
...entry,
totalAmount:
entry.totalAmount ??
entry.cvePurlLow +
entry.cvePurlMedium +
entry.cvePurlHigh +
entry.cvePurlCritical,
fixableLow: entry.fixableLow ?? 2,
fixableMedium: entry.fixableMedium ?? 7,
fixableHigh: entry.fixableHigh ?? entry.high * 9,
fixableCritical: entry.fixableCritical ?? entry.critical * 15,
cvePurlFixableLow:
entry.cvePurlFixableLow ?? Math.floor(entry.cvePurlLow / 2),
cvePurlFixableMedium:
entry.cvePurlFixableMedium ?? Math.floor(entry.cvePurlMedium / 4),
cvePurlFixableHigh:
entry.cvePurlFixableHigh ?? Math.floor(entry.cvePurlHigh / 3),
cvePurlFixableCritical:
entry.cvePurlFixableCritical ?? Math.floor(entry.cvePurlCritical / 2),
Comment on lines +333 to +344
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

withMockFixableRiskHistory generates hard-coded / derived "fixable" values (e.g. constants like 2, 7, and multipliers like entry.high * 9). Since this function is exported from a shared util and is now used in the overview page, it can easily leak mocked data into production UX. Prefer removing it from shared utils (or clearly scoping it to dev/test) and rely on backend-provided fields instead.

Suggested change
fixableLow: entry.fixableLow ?? 2,
fixableMedium: entry.fixableMedium ?? 7,
fixableHigh: entry.fixableHigh ?? entry.high * 9,
fixableCritical: entry.fixableCritical ?? entry.critical * 15,
cvePurlFixableLow:
entry.cvePurlFixableLow ?? Math.floor(entry.cvePurlLow / 2),
cvePurlFixableMedium:
entry.cvePurlFixableMedium ?? Math.floor(entry.cvePurlMedium / 4),
cvePurlFixableHigh:
entry.cvePurlFixableHigh ?? Math.floor(entry.cvePurlHigh / 3),
cvePurlFixableCritical:
entry.cvePurlFixableCritical ?? Math.floor(entry.cvePurlCritical / 2),

Copilot uses AI. Check for mistakes.
}));
};

export const reduceRiskHistories = (
histories: RiskHistory[][],
): Array<ReleaseRiskHistory> => {
Expand All @@ -329,10 +355,15 @@ export const reduceRiskHistories = (
acc.cvePurlMedium += curr.cvePurlMedium;
acc.cvePurlHigh += curr.cvePurlHigh;
acc.cvePurlCritical += curr.cvePurlCritical;
acc.cvePurlFixableLow += curr.cvePurlFixableLow ?? 0;
acc.cvePurlFixableMedium += curr.cvePurlFixableMedium ?? 0;
acc.cvePurlFixableHigh += curr.cvePurlFixableHigh ?? 0;
acc.cvePurlFixableCritical += curr.cvePurlFixableCritical ?? 0;
acc.cvePurlLowCvss += curr.cvePurlLowCvss;
acc.cvePurlMediumCvss += curr.cvePurlMediumCvss;
acc.cvePurlHighCvss += curr.cvePurlHighCvss;
acc.cvePurlCriticalCvss += curr.cvePurlCriticalCvss;
acc.totalAmount = (acc.totalAmount ?? 0) + (curr.totalAmount ?? 0);
return acc;
},
{
Expand All @@ -344,10 +375,15 @@ export const reduceRiskHistories = (
cvePurlMedium: 0,
cvePurlHigh: 0,
cvePurlCritical: 0,
cvePurlFixableLow: 0,
cvePurlFixableMedium: 0,
cvePurlFixableHigh: 0,
cvePurlFixableCritical: 0,
cvePurlLowCvss: 0,
cvePurlMediumCvss: 0,
cvePurlHighCvss: 0,
cvePurlCriticalCvss: 0,
totalAmount: 0,
} as RiskHistory,
);
});
Expand Down
Loading