feat: [AH-341]: support download SLSA report from artifact registry (#2771)

* feat: [AH-341]: update spelling mistake
* Merge branch 'feat-AH-341' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness into feat-AH-341
* feat: [AH-341]: update api schema to fix type errors
* feat: [AH-341]: support download SLSA report from artifact registry
* feat: [AH-341]: update api schema to fix type errors
* feat: [AH-341]: support download SLSA report from artifact registry
This commit is contained in:
Shivanand Sonnad 2024-10-07 12:03:26 +00:00 committed by Harness
parent f62a6e5e68
commit 8b53e5b92a
11 changed files with 138 additions and 18 deletions

View File

@ -51,7 +51,8 @@
"@codemirror/view": "^6.9.6",
"@harnessio/design-system": "^2.1.1",
"@harnessio/icons": "^2.1.7",
"@harnessio/react-har-service-client": "^0.0.22",
"@harnessio/react-har-service-client": "^0.0.23",
"@harnessio/react-ssca-manager-client": "^0.65.0",
"@harnessio/uicore": "^4.1.2",
"@tanstack/react-query": "4.20.4",
"@types/dompurify": "^3.0.2",

View File

@ -14,9 +14,10 @@
* limitations under the License.
*/
import React, { PropsWithChildren, Suspense, useEffect, useRef } from 'react'
import React, { PropsWithChildren, Suspense, useEffect, useMemo, useRef } from 'react'
import { Page } from '@harnessio/uicore'
import { HARServiceAPIClient } from '@harnessio/react-har-service-client'
import { SSCAManagerAPIClient } from '@harnessio/react-ssca-manager-client'
import { QueryClientProvider } from '@tanstack/react-query'
import { StringsContextProvider } from '@ar/frameworks/strings/StringsContextProvider'
@ -57,10 +58,10 @@ export default function ChildApp(props: PropsWithChildren<MFEAppProps>): React.R
} = props
const { ModalProvider } = customComponents
const appStoreData = React.useContext(parentContextObj.appStoreContext)
useRef<HARServiceAPIClient>(
new HARServiceAPIClient({
const apiClientOptions = useMemo(
() => ({
responseInterceptor: (response: Response): Response => {
if (!response.ok && response.status === 401) {
on401()
@ -71,7 +72,7 @@ export default function ChildApp(props: PropsWithChildren<MFEAppProps>): React.R
urlInterceptor: (url: string) => {
return customUtils.getApiBaseUrl(url)
},
requestInterceptor(request) {
requestInterceptor(request: Request) {
request.headers.delete('Authorization')
// add custom headers if available
const customHeader = customUtils.getCustomHeaders()
@ -80,9 +81,13 @@ export default function ChildApp(props: PropsWithChildren<MFEAppProps>): React.R
})
return request
}
})
}),
[]
)
useRef<HARServiceAPIClient>(new HARServiceAPIClient(apiClientOptions))
useRef<SSCAManagerAPIClient>(new SSCAManagerAPIClient(apiClientOptions))
useEffect(
() => () => {
if (typeof appStoreData.updateAppStore === 'function' && parent !== Parent.Enterprise) {

View File

@ -100,6 +100,7 @@ export default function DockerVersionOverviewCards() {
artifactId: responseData.sbomDetails?.artifactId
})
}}
provenanceId={defaultTo(responseData.slsaDetails?.provenanceId, '')}
className={css.card}
totalComponents={defaultTo(responseData.sbomDetails?.componentsCount, 0)}
allowListCount={defaultTo(responseData.sbomDetails?.allowListViolations, 0)}

View File

@ -25,3 +25,8 @@
}
}
}
.downloadSlsaBtn {
--font-size: 12px !important;
--padding: 0 !important;
}

View File

@ -18,4 +18,5 @@
// This is an auto-generated file
export declare const column: string
export declare const container: string
export declare const downloadSlsaBtn: string
export declare const primaryColumn: string

View File

@ -16,10 +16,12 @@
import React from 'react'
import classNames from 'classnames'
import { Card, Layout, Text } from '@harnessio/uicore'
import { Button, ButtonSize, ButtonVariation, Card, Layout, Text } from '@harnessio/uicore'
import { Color, FontVariation } from '@harnessio/design-system'
import { killEvent } from '@ar/common/utils'
import { useStrings } from '@ar/frameworks/strings'
import useDownloadSLSAProvenance from '@ar/pages/version-details/hooks/useDownloadSLSAProvenance'
import SecurityItem from '../SecurityTestsCard/SecurityItem'
import { SecurityTestSatus } from '../SecurityTestsCard/types'
@ -33,13 +35,16 @@ interface SupplyChainCardProps {
allowListCount: number
denyListCount: number
sbomScore: string | number
provenanceId: string
onClick?: () => void
}
export default function SupplyChainCard(props: SupplyChainCardProps) {
const { title, totalComponents, allowListCount, denyListCount, className, sbomScore, onClick } = props
const { title, totalComponents, allowListCount, denyListCount, className, sbomScore, onClick, provenanceId } = props
const { getString } = useStrings()
const { download, loading } = useDownloadSLSAProvenance()
return (
<Card className={className} onClick={onClick}>
<Layout.Vertical>
@ -59,13 +64,18 @@ export default function SupplyChainCard(props: SupplyChainCardProps) {
value={sbomScore}
status={SecurityTestSatus.Green}
/>
<Text
flex={{ alignItems: 'center' }}
font={{ variation: FontVariation.SMALL }}
<Button
className={css.downloadSlsaBtn}
size={ButtonSize.SMALL}
rightIcon="download-manifests"
iconProps={{ size: 18 }}>
variation={ButtonVariation.LINK}
loading={loading}
onClick={evt => {
killEvent(evt)
download(provenanceId)
}}>
{getString('versionDetails.cards.supplyChain.slsaProvenance')}
</Text>
</Button>
</Layout.Vertical>
</Layout.Horizontal>
<Layout.Horizontal className={css.container}>

View File

@ -0,0 +1,56 @@
/*
* Copyright 2024 Harness, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useState } from 'react'
import { defaultTo } from 'lodash-es'
import { useToaster } from '@harnessio/uicore'
import { getProvenance } from '@harnessio/react-ssca-manager-client'
import { useAppStore } from '@ar/hooks'
import { useStrings } from '@ar/frameworks/strings'
import { downloadRawFile } from '@ar/utils/downloadRawFile'
export default function useDownloadSLSAProvenance() {
const [loading, setLoading] = useState(false)
const { scope } = useAppStore()
const { showError } = useToaster()
const { getString } = useStrings()
const download = async (provenanceId: string) => {
setLoading(true)
return getProvenance({
org: defaultTo(scope.orgIdentifier, ''),
project: defaultTo(scope.projectIdentifier, ''),
provenance: provenanceId
})
.then(data => {
const content = defaultTo(data.content.provenance, '')
if (!content) {
throw new Error(getString('versionDetails.cards.supplyChain.provenanceDataNotAvailable'))
}
return downloadRawFile(content, `provenance_${provenanceId}.json`)
})
.catch((err: Error) => {
showError(defaultTo(err?.message, err))
return false
})
.finally(() => {
setLoading(false)
})
}
return { download, loading }
}

View File

@ -25,6 +25,7 @@ cards:
totalComponents: Total Dependencies
sbomScore: SBOM Score
slsaProvenance: SLSA Provenance
provenanceDataNotAvailable: Provenance data is not available
allowList: Allow List
denyListViolation: Deny List Violation
container:

View File

@ -161,6 +161,7 @@ export interface StringsMap {
'versionDetails.cards.securityTests.totalCount': string
'versionDetails.cards.supplyChain.allowList': string
'versionDetails.cards.supplyChain.denyListViolation': string
'versionDetails.cards.supplyChain.provenanceDataNotAvailable': string
'versionDetails.cards.supplyChain.sbomScore': string
'versionDetails.cards.supplyChain.slsaProvenance': string
'versionDetails.cards.supplyChain.title': string

View File

@ -0,0 +1,34 @@
/*
* Copyright 2024 Harness, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const downloadRawFile = (content: string, filename: string, fileType = 'text/json') => {
return new Promise<void>((resolve, reject) => {
try {
const url = URL.createObjectURL(new Blob([content], { type: fileType }))
const a = document.createElement('a')
a.href = url
a.download = filename
a.click()
setTimeout(() => {
URL.revokeObjectURL(url)
a.remove()
}, 150)
resolve()
} catch (err) {
reject(err)
}
})
}

View File

@ -1654,10 +1654,15 @@
resolved "https://registry.yarnpkg.com/@harnessio/icons/-/icons-2.1.7.tgz#21c37bad4291e1ed4ea730f6ed1f7612934e8b3f"
integrity sha512-+vugtcJR47pQZj2IMYRt7XSNpjW/m9iSeLjQMG2Hja3Hq9J+zfDsXh2aRor3ICrLSrkQtRWpBbWn+WmsoN3lVg==
"@harnessio/react-har-service-client@^0.0.22":
version "0.0.22"
resolved "https://registry.yarnpkg.com/@harnessio/react-har-service-client/-/react-har-service-client-0.0.22.tgz#f946ef4378fecc5ecbffa46ea664446b93b01f79"
integrity sha512-MzTcA3xcaQbuYVDe9v5gPaHSCUs+7gpLt4F5/Bh26hbhzPSpGK2nxqh37sxIq3fVNkpGWXpScrnCxV1FDGNHvw==
"@harnessio/react-har-service-client@^0.0.23":
version "0.0.23"
resolved "https://registry.yarnpkg.com/@harnessio/react-har-service-client/-/react-har-service-client-0.0.23.tgz#ea4f5705155b9d7a87a475f5a45adb9d72c2d8ab"
integrity sha512-fXcW3WylHLCpPf1Ce/z1GoKcy/1jPk7tzlXMUzqTnooFlnhpOPV5fXOpVyGP8i8mmKIW17vsLLedgf+LgRhD3w==
"@harnessio/react-ssca-manager-client@^0.65.0":
version "0.65.0"
resolved "https://registry.yarnpkg.com/@harnessio/react-ssca-manager-client/-/react-ssca-manager-client-0.65.0.tgz#8088869e282c5268bf1fefb9715652e0fc1a8940"
integrity sha512-sNLDf1lyBfdzD9TqlrsMib61YffrEpb5WJwhRKuPGKD9Po0peAqE6NGLHVFbyra4tRGvvg8gnNpzzp4hMBYeCQ==
"@harnessio/uicore@^4.1.2":
version "4.1.2"