diff --git a/src/components/asset/asset-form/AssetFormVulnsManagement.tsx b/src/components/asset/asset-form/AssetFormVulnsManagement.tsx index 0ae07396..93addb12 100644 --- a/src/components/asset/asset-form/AssetFormVulnsManagement.tsx +++ b/src/components/asset/asset-form/AssetFormVulnsManagement.tsx @@ -432,52 +432,118 @@ const EnableTicketRange: FunctionComponent = ({ form }) => { ); }; -const PublicUrlsSection: FunctionComponent<{ +const ArtifactInputCVSSBadge: FunctionComponent<{ assetId: string; devguardApiUrl: string; orgSlug: string; projectSlug?: string; assetSlug?: string; + selectedVersionSlug: string; + selectedArtifact: string; + setSelectedVersionSlug: (v: string) => void; + setSelectedArtifact: (v: string) => void; refs: AssetVersionDTO[]; - copyable: boolean; + artifacts?: ArtifactDTO[]; + selectedVersion?: AssetVersionDTO; + purlValidation: { isValid: boolean; warning?: string }; + basePath?: string; }> = ({ assetId, devguardApiUrl, orgSlug, projectSlug, assetSlug, - copyable, + selectedVersionSlug, + selectedArtifact, + setSelectedVersionSlug, + setSelectedArtifact, + refs, + artifacts, + selectedVersion, + purlValidation, + basePath, }) => { - const [selectedVersionSlug, setSelectedVersionSlug] = useState(""); - const [selectedArtifact, setSelectedArtifact] = useState(""); - - const refs = useActiveAsset().refs; - - const { data: artifacts } = useSWR( - selectedVersionSlug && assetSlug && projectSlug - ? `/organizations/${orgSlug}/projects/${projectSlug}/assets/${assetSlug}/refs/${selectedVersionSlug}/artifacts` - : null, - fetcher, - ); - - // Reset artifact selection when version changes - React.useEffect(() => { - setSelectedArtifact(""); - }, [selectedVersionSlug]); + return ( +
+
+ + +
- // Get the selected version to check validation - const selectedVersion = refs.find((ref) => ref.slug === selectedVersionSlug); +
+ + +
- // Validate artifact name + version creates a valid PURL - const purlValidation = React.useMemo(() => { - return validateArtifactNameAgainstPurlSpec(selectedArtifact); - }, [selectedArtifact]); + {selectedArtifact && selectedVersion && !purlValidation.isValid && ( + + + {purlValidation.warning} + + )} - const basePath = - selectedVersionSlug && selectedArtifact - ? `${devguardApiUrl}/api/v1/public/${assetId}/refs/${selectedVersionSlug}/artifacts/${encodeURIComponent(selectedArtifact)}` - : undefined; + {basePath && ( +
+ + CVSS Badge +
+ )} +
+ ); +}; +const PublicUrlsSection: FunctionComponent<{ + assetId: string; + devguardApiUrl: string; + orgSlug: string; + copyable: boolean; + basePath?: string; +}> = ({ devguardApiUrl, orgSlug, copyable, basePath }) => { const urls = [ { label: "VeX-URL (Always up to date vulnerability information)", @@ -511,66 +577,6 @@ const PublicUrlsSection: FunctionComponent<{ return ( <> -
-
- - -
- -
- - -
-
- - {selectedArtifact && selectedVersion && !purlValidation.isValid && ( - - - {purlValidation.warning} - - )} - {urls.map((url) => (
))} - {basePath && ( - CVSS Badge - )} ); }; @@ -607,6 +606,43 @@ export const AssetFormVulnsManagement: FunctionComponent = ({ const org = useActiveOrg(); const project = useActiveProject(); const asset = project?.assets.find((a) => a.id === assetId); + const refs = useActiveAsset().refs; + const defaultBranch = refs.find((ref) => ref.defaultBranch); + const [selectedVersionSlug, setSelectedVersionSlug] = useState( + defaultBranch?.slug ?? "", + ); + const [selectedArtifact, setSelectedArtifact] = useState(""); + const orgSlug = org.slug; + const projectSlug = project?.slug; + const assetSlug = asset?.slug; + const { data: artifacts } = useSWR( + selectedVersionSlug && assetSlug && projectSlug + ? `/organizations/${orgSlug}/projects/${projectSlug}/assets/${assetSlug}/refs/${selectedVersionSlug}/artifacts` + : null, + fetcher, + ); + + // Auto-select first artifact when artifacts load or version changes + React.useEffect(() => { + if (artifacts?.length) { + setSelectedArtifact(artifacts[0].artifactName); + } else { + setSelectedArtifact(""); + } + }, [artifacts]); + + // Get the selected version to check validation + const selectedVersion = refs.find((ref) => ref.slug === selectedVersionSlug); + + // Validate artifact name + version creates a valid PURL + const purlValidation = React.useMemo(() => { + return validateArtifactNameAgainstPurlSpec(selectedArtifact); + }, [selectedArtifact]); + + const basePath = + selectedVersionSlug && selectedArtifact + ? `${devguardApiUrl}/api/v1/public/${assetId}/refs/${selectedVersionSlug}/artifacts/${encodeURIComponent(selectedArtifact)}` + : undefined; return ( <>
@@ -649,6 +685,22 @@ export const AssetFormVulnsManagement: FunctionComponent = ({ By enabling this option, your vulnerability endpoints are made publicly accessible. Select an asset version and artifact below to construct the public URLs. + {field.value && ( @@ -659,11 +711,9 @@ export const AssetFormVulnsManagement: FunctionComponent = ({