feat(compliance): add resource metadata tab inside req find (#11187)

This commit is contained in:
Pedro Martín authored and GitHub committed 2026-05-21 15:09:43 +02:00
1 parent e55d1d470e
commit dbbefd0558
14 files changed
+569 -63

No files matched your search

@@ -215,6 +215,7 @@ vi.mock("@/components/shared/query-code-editor", () => ({
HCL: "hcl",
BICEP: "bicep",
YAML: "yaml",
JSON: "json",
},
QueryCodeEditor: ({
ariaLabel,
@@ -426,6 +427,8 @@ const mockFinding: ResourceDrawerFinding = {
resourceRegion: "us-east-1",
resourceType: "Bucket",
resourceGroup: "default",
resourceDetails: null,
resourceMetadata: null,
providerType: "aws",
providerAlias: "prod",
providerUid: "123456789",
@@ -1764,3 +1767,160 @@ describe("ResourceDetailDrawerContent — other findings delta/muted indicator",
});
});
});
describe("ResourceDetailDrawerContent — Metadata tab", () => {
const getMetadataEditor = () =>
screen
.queryAllByTestId("query-code-editor")
.find(
(editor) =>
editor.getAttribute("data-aria-label") === "Resource metadata",
);
it("should render a Metadata tab trigger", () => {
// Given/When
render(
<ResourceDetailDrawerContent
isLoading={false}
isNavigating={false}
checkMeta={mockCheckMeta}
currentIndex={0}
totalResources={1}
currentFinding={mockFinding}
otherFindings={[]}
onNavigatePrev={vi.fn()}
onNavigateNext={vi.fn()}
onMuteComplete={vi.fn()}
/>,
);
// Then
expect(
screen.getByRole("button", { name: "Resource Metadata / Evidence" }),
).toBeInTheDocument();
});
it("should render the resource metadata as formatted JSON and copy it to the clipboard", async () => {
// Given
const user = userEvent.setup();
const findingWithMetadata: ResourceDrawerFinding = {
...mockFinding,
resourceDetails: "Python",
resourceMetadata: {
VulnerabilityID: "CVE-2026-0001",
PkgName: "requests",
},
};
render(
<ResourceDetailDrawerContent
isLoading={false}
isNavigating={false}
checkMeta={mockCheckMeta}
currentIndex={0}
totalResources={1}
currentFinding={findingWithMetadata}
otherFindings={[]}
onNavigatePrev={vi.fn()}
onNavigateNext={vi.fn()}
onMuteComplete={vi.fn()}
/>,
);
// Then — Details section + JSON editor are rendered
expect(screen.getByText("Details:")).toBeInTheDocument();
expect(screen.getByText("Python")).toBeInTheDocument();
const metadataEditor = getMetadataEditor();
expect(metadataEditor).toBeDefined();
expect(metadataEditor).toHaveAttribute("data-language", "json");
expect(metadataEditor?.textContent).toContain("CVE-2026-0001");
// When — copy the metadata JSON
await user.click(
screen.getByRole("button", { name: "Copy Resource metadata" }),
);
// Then
expect(mockClipboardWriteText).toHaveBeenCalledWith(
JSON.stringify(findingWithMetadata.resourceMetadata, null, 2),
);
});
it("should parse stringified resource metadata", () => {
// Given
const findingWithStringMetadata: ResourceDrawerFinding = {
...mockFinding,
resourceMetadata: '{"PkgName":"requests"}',
};
render(
<ResourceDetailDrawerContent
isLoading={false}
isNavigating={false}
checkMeta={mockCheckMeta}
currentIndex={0}
totalResources={1}
currentFinding={findingWithStringMetadata}
otherFindings={[]}
onNavigatePrev={vi.fn()}
onNavigateNext={vi.fn()}
onMuteComplete={vi.fn()}
/>,
);
// Then
expect(getMetadataEditor()?.textContent).toContain("requests");
});
it("should show an empty state when no metadata or details are available", () => {
// Given/When
render(
<ResourceDetailDrawerContent
isLoading={false}
isNavigating={false}
checkMeta={mockCheckMeta}
currentIndex={0}
totalResources={1}
currentFinding={mockFinding}
otherFindings={[]}
onNavigatePrev={vi.fn()}
onNavigateNext={vi.fn()}
onMuteComplete={vi.fn()}
/>,
);
// Then
expect(
screen.getByText("No metadata available for this resource."),
).toBeInTheDocument();
expect(getMetadataEditor()).toBeUndefined();
});
it("should show a metadata skeleton while navigating", () => {
// Given/When
render(
<ResourceDetailDrawerContent
isLoading={false}
isNavigating
checkMeta={mockCheckMeta}
currentIndex={0}
totalResources={2}
currentResource={mockResourceRow}
currentFinding={mockFinding}
otherFindings={[]}
onNavigatePrev={vi.fn()}
onNavigateNext={vi.fn()}
onMuteComplete={vi.fn()}
/>,
);
// Then
expect(
screen.getByTestId("metadata-navigation-skeleton"),
).toBeInTheDocument();
expect(
screen.queryByText("No metadata available for this resource."),
).not.toBeInTheDocument();
});
});
@@ -53,6 +53,7 @@ import {
QueryCodeEditor,
type QueryEditorLanguage,
} from "@/components/shared/query-code-editor";
import { ResourceMetadataPanel } from "@/components/shared/resource-metadata-panel";
import { CodeSnippet } from "@/components/ui/code-snippet/code-snippet";
import { CustomLink } from "@/components/ui/custom/custom-link";
import { DateWithTime } from "@/components/ui/entities/date-with-time";
@@ -845,6 +846,9 @@ export function ResourceDetailDrawerContent({
<TabsList>
<TabsTrigger value="overview">Finding Overview</TabsTrigger>
<TabsTrigger value="remediation">Remediation</TabsTrigger>
<TabsTrigger value="metadata">
Resource Metadata / Evidence
</TabsTrigger>
<TabsTrigger value="other-findings">
Findings for this resource
</TabsTrigger>
@@ -1072,6 +1076,21 @@ export function ResourceDetailDrawerContent({
)}
</TabsContent>
{/* Metadata */}
<TabsContent
value="metadata"
className="minimal-scrollbar flex flex-col gap-4 overflow-y-auto"
>
{isNavigating ? (
<MetadataNavigationSkeleton />
) : (
<ResourceMetadataPanel
metadata={f?.resourceMetadata}
details={f?.resourceDetails}
/>
)}
</TabsContent>
{/* Findings for this resource */}
<TabsContent
value="other-findings"
@@ -1400,6 +1419,21 @@ function EventsNavigationSkeleton() {
);
}
function MetadataNavigationSkeleton() {
return (
<div
className="flex flex-col gap-4"
data-testid="metadata-navigation-skeleton"
aria-hidden="true"
>
<Card variant="inner">
<OverviewCardSkeleton lineWidths={["w-20", "w-full", "w-3/4"]} />
</Card>
<Skeleton className="h-56 w-full rounded" />
</div>
);
}
function OtherFindingRow({
finding,
isOptimisticallyMuted,
@@ -86,6 +86,8 @@ function makeDrawerFinding(
resourceRegion: "us-east-1",
resourceType: "bucket",
resourceGroup: "default",
resourceDetails: null,
resourceMetadata: null,
providerType: "aws",
providerAlias: "prod",
providerUid: "123",