-
Notifications
You must be signed in to change notification settings - Fork 56
feat: trivy Image scanning module Integration #982
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 18 commits
04435aa
43880b8
89bd6d1
ae3e64b
f720d34
c66362b
92989c9
dc62b98
c716b81
8716dce
651385e
9c0aec8
f592cd3
974afe3
ad980e1
d1c9088
a368abc
6b8dc81
9444f82
4bf1f97
3287975
9eedba3
3b913a4
cd362fe
aa386c9
eaa577d
d6f2d29
44802bd
9f9c4c3
edd64b3
1d845b8
682c304
977bc61
ed43003
7e29d5d
bb98bad
f8eaa5f
eb4326b
4d85725
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -103,10 +103,11 @@ export const getCIConfigList = (envID: string, appIds: string): Promise<CIConfig | |
| getCIConfigMin(envID, appIds), | ||
| getModuleInfo(ModuleNameMap.SECURITY), | ||
| getModuleConfigured(ModuleNameMap.BLOB_STORAGE), | ||
| ]).then(([ciConfig, securityInfo, moduleConfig]) => { | ||
| getModuleInfo(ModuleNameMap.SECURITY_TRIVY), | ||
| ]).then(([ciConfig, securityInfo, moduleConfig,trivysecurityInfo]) => { | ||
| return { | ||
| pipelineList: ciConfig.result, | ||
| securityModuleInstalled: securityInfo?.result?.status === ModuleStatus.INSTALLED, | ||
| securityModuleInstalled: (securityInfo?.result?.status === ModuleStatus.INSTALLED || trivysecurityInfo?.result?.status === ModuleStatus.INSTALLED), | ||
|
||
| blobStorageConfigured: moduleConfig?.result?.enabled, | ||
| } | ||
| }) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -233,7 +233,8 @@ export default function CIPipeline({ | |
| const getSecurityModuleStatus = async (): Promise<void> => { | ||
| try { | ||
| const { result } = await getModuleInfo(ModuleNameMap.SECURITY) | ||
| if (result?.status === ModuleStatus.INSTALLED) { | ||
| const { result:result2 } =await getModuleInfo(ModuleNameMap.SECURITY_TRIVY) | ||
|
||
| if (result?.status === ModuleStatus.INSTALLED || result2?.status === ModuleStatus.INSTALLED ) { | ||
| setSecurityModuleInstalled(true) | ||
| } | ||
| } catch (error) {} | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,13 +2,13 @@ import React, { useState, useEffect, useMemo } from 'react' | |
| import { showError, Progressing, Reload, GenericEmptyState } from '@devtron-labs/devtron-fe-common-lib' | ||
| import { getCIPipelines, getCIHistoricalStatus, getTriggerHistory, getArtifact } from '../../service' | ||
| import { useScrollable, useAsync, useInterval, mapByKey, asyncWrap } from '../../../common' | ||
| import { URLS, ModuleNameMap } from '../../../../config' | ||
| import { URLS, ModuleNameMap, SCAN_TOOL_ID_TRIVY } from '../../../../config' | ||
| import { NavLink, Switch, Route, Redirect } from 'react-router-dom' | ||
| import { useRouteMatch, useParams, useHistory, generatePath } from 'react-router' | ||
| import { BuildDetails, CIPipeline, HistoryLogsType, SecurityTabType } from './types' | ||
| import { ReactComponent as Down } from '../../../../assets/icons/ic-dropdown-filled.svg' | ||
| import { getLastExecutionByArtifactId } from '../../../../services/service' | ||
| import { ScanDisabledView, ImageNotScannedView, NoVulnerabilityView, CIRunningView } from './cIDetails.util' | ||
| import { ScanDisabledView, ImageNotScannedView, CIRunningView } from './cIDetails.util' | ||
| import './ciDetails.scss' | ||
| import { getModuleInfo } from '../../../v2/devtronStackManager/DevtronStackManager.service' | ||
| import { ModuleStatus } from '../../../v2/devtronStackManager/DevtronStackManager.type' | ||
|
|
@@ -20,6 +20,9 @@ import Artifacts from '../cicdHistory/Artifacts' | |
| import { CICDSidebarFilterOptionType, History, HistoryComponentType } from '../cicdHistory/types' | ||
| import LogsRenderer from '../cicdHistory/LogsRenderer' | ||
| import { EMPTY_STATE_STATUS } from '../../../../config/constantMessaging' | ||
| import { ReactComponent as Clair } from '../../../../assets/icons/ic-clair.svg' | ||
| import { ReactComponent as Trivy } from '../../../../assets/icons/ic-trivy.svg' | ||
| import novulnerability from '../../../../assets/img/ic-vulnerability-not-found.svg'; | ||
|
|
||
| const terminalStatus = new Set(['succeeded', 'failed', 'error', 'cancelled', 'nottriggered', 'notbuilt']) | ||
| let statusSet = new Set(['starting', 'running', 'pending']) | ||
|
|
@@ -41,6 +44,7 @@ export default function CIDetails({ isJobView }: { isJobView?: boolean }) { | |
| getCIPipelines(+appId), | ||
| getModuleInfo(ModuleNameMap.SECURITY), | ||
| getModuleConfigured(ModuleNameMap.BLOB_STORAGE), | ||
| getModuleInfo(ModuleNameMap.SECURITY_TRIVY) | ||
| ]), | ||
| [appId], | ||
| ) | ||
|
|
@@ -162,8 +166,9 @@ export default function CIDetails({ isJobView }: { isJobView?: boolean }) { | |
| synchroniseState={synchroniseState} | ||
| triggerHistory={triggerHistory} | ||
| isSecurityModuleInstalled={ | ||
| initDataResults[1]?.['value']?.['result']?.status === | ||
| ModuleStatus.INSTALLED || false | ||
| (initDataResults[1]?.['value']?.['result']?.status === | ||
|
||
| ModuleStatus.INSTALLED|| initDataResults[3]?.['value']?.['result']?.status === | ||
| ModuleStatus.INSTALLED )|| false | ||
| } | ||
| isBlobStorageConfigured={ | ||
| initDataResults[2]?.['value']?.['result']?.enabled || false | ||
|
|
@@ -405,6 +410,7 @@ const SecurityTab = ({ ciPipelineId, artifactId, status, appIdFromParent }: Secu | |
| scanned: false, | ||
| isLoading: !!artifactId, | ||
| isError: false, | ||
| ScanToolId:0, | ||
|
||
| }) | ||
| const { appId } = useParams<{ appId: string }>() | ||
| const { push } = useHistory() | ||
|
|
@@ -419,6 +425,7 @@ const SecurityTab = ({ ciPipelineId, artifactId, status, appIdFromParent }: Secu | |
| scanned: result.scanned, | ||
| isLoading: false, | ||
| isError: false, | ||
| ScanToolId:result.scanToolId | ||
|
||
| }) | ||
| } catch (error) { | ||
| // showError(error); | ||
|
|
@@ -472,7 +479,13 @@ const SecurityTab = ({ ciPipelineId, artifactId, status, appIdFromParent }: Secu | |
| return <ImageNotScannedView /> | ||
| } | ||
| } else if (artifactId && securityData.scanned && !securityData.vulnerabilities.length) { | ||
| return <NoVulnerabilityView /> | ||
| return ( | ||
|
||
| <div className='flex h-100'> | ||
| <GenericEmptyState image={novulnerability} title={EMPTY_STATE_STATUS.CI_DEATILS_NO_VULNERABILITY_FOUND} children={<span className="flex workflow__header dc__border-radius-24 bcn-0"> | ||
| Scanned By {securityData.ScanToolId===SCAN_TOOL_ID_TRIVY ? 'Trivy ' : 'Clair '} {securityData.ScanToolId===SCAN_TOOL_ID_TRIVY ? <Trivy className="h-20 w-20" /> : <Clair className="h-20 w-20" />} | ||
| </span>} /> | ||
| </div> | ||
| ) | ||
| } | ||
|
|
||
| return ( | ||
|
|
@@ -497,7 +510,10 @@ const SecurityTab = ({ ciPipelineId, artifactId, status, appIdFromParent }: Secu | |
| {severityCount.critical === 0 && severityCount.moderate === 0 && severityCount.low !== 0 ? ( | ||
| <span className="dc__fill-low">{severityCount.low} Low</span> | ||
| ) : null} | ||
| <div className="security-scan__type">post build execution</div> | ||
| <div className="security-scan__type flex"> | ||
| Scanned By {securityData.ScanToolId=== SCAN_TOOL_ID_TRIVY? 'Trivy' : 'Clair'} | ||
| {securityData.ScanToolId===SCAN_TOOL_ID_TRIVY ? <Trivy /> : <Clair/>} | ||
| </div> | ||
| </div> | ||
| {isCollapsed ? ( | ||
| '' | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,8 @@ import { ReactComponent as WarningIcon } from '../../../../assets/icons/ic-warni | |
| import { ReactComponent as BackIcon } from '../../../../assets/icons/ic-arrow-backward.svg' | ||
| import { ReactComponent as BotIcon } from '../../../../assets/icons/ic-bot.svg' | ||
| import { ReactComponent as World } from '../../../../assets/icons/ic-world.svg' | ||
| import { ReactComponent as Clair } from '../../../../assets/icons/ic-clair.svg' | ||
| import { ReactComponent as Trivy } from '../../../../assets/icons/ic-trivy.svg' | ||
| import { ReactComponent as Failed } from '../../../../assets/icons/ic-rocket-fail.svg' | ||
| import { ReactComponent as InfoIcon } from '../../../../assets/icons/info-filled.svg' | ||
| import play from '../../../../assets/icons/misc/arrow-solid-right.svg' | ||
|
|
@@ -42,7 +44,7 @@ import { CDButtonLabelMap, getCommonConfigSelectStyles, TriggerViewContext } fro | |
| import { getLatestDeploymentConfig, getRecentDeploymentConfig, getSpecificDeploymentConfig } from '../../service' | ||
| import GitCommitInfoGeneric from '../../../common/GitCommitInfoGeneric' | ||
| import { getModuleInfo } from '../../../v2/devtronStackManager/DevtronStackManager.service' | ||
| import { ModuleNameMap } from '../../../../config' | ||
| import { ModuleNameMap, SCAN_TOOL_ID_TRIVY } from '../../../../config' | ||
| import { ModuleStatus } from '../../../v2/devtronStackManager/DevtronStackManager.type' | ||
| import { DropdownIndicator, Option } from '../../../v2/common/ReactSelect.utils' | ||
| import { | ||
|
|
@@ -153,7 +155,8 @@ export class CDMaterial extends Component<CDMaterialProps, CDMaterialState> { | |
| async getSecurityModuleStatus(): Promise<void> { | ||
| try { | ||
| const { result } = await getModuleInfo(ModuleNameMap.SECURITY) | ||
| if (result?.status === ModuleStatus.INSTALLED) { | ||
| const {result:result2} =await getModuleInfo(ModuleNameMap.SECURITY_TRIVY) | ||
|
||
| if (result?.status === ModuleStatus.INSTALLED|| result2?.status === ModuleStatus.INSTALLED) { | ||
| this.setState({ isSecurityModuleInstalled: true }) | ||
| } | ||
| } catch (error) {} | ||
|
|
@@ -221,15 +224,20 @@ export class CDMaterial extends Component<CDMaterialProps, CDMaterialState> { | |
| ) | ||
| } else if (!mat.vulnerabilitiesLoading && mat.vulnerabilities.length === 0) { | ||
| return ( | ||
| <div className="security-tab-empty"> | ||
| <p className="security-tab-empty__title">No vulnerabilities Found</p> | ||
| <div className="security-tab-empty summary-view__card"> | ||
| <p className="security-tab-empty__title">You’re secure!</p> | ||
|
||
| <p className="">No security vulnerability found for this image.</p> | ||
| <p className="security-tab-empty__subtitle">{mat.lastExecution}</p> | ||
| <p className='workflow__header dc__border-radius-24 bcn-0'>Scanned By {mat.scanToolId===SCAN_TOOL_ID_TRIVY ?'Trivy':'Clair'}{mat.scanToolId===SCAN_TOOL_ID_TRIVY? <Trivy className='h-20 w-20'/>:<Clair className='h-20 w-20'/>} </p> | ||
|
||
| </div> | ||
| ) | ||
| } else | ||
| return ( | ||
| <div className="security-tab"> | ||
| <p className="security-tab__last-scanned">Scanned on {mat.lastExecution} </p> | ||
| <div className='flexbox dc__content-space'> | ||
|
||
| <span className="flex left security-tab__last-scanned ">Scanned on {mat.lastExecution} </span> | ||
| <span className='flex right'>Scanned By {mat.scanToolId===SCAN_TOOL_ID_TRIVY?'Trivy':'Clair'}{mat.scanToolId===SCAN_TOOL_ID_TRIVY? <Trivy className='h-20 w-20'/>:<Clair className='h-20 w-20'/>} </span> | ||
| </div> | ||
| <ScanVulnerabilitiesTable vulnerabilities={mat.vulnerabilities} /> | ||
| </div> | ||
| ) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -106,6 +106,7 @@ const NavigationList = [ | |
| iconClass: 'nav-security', | ||
| icon: SecurityIcon, | ||
| moduleName: ModuleNameMap.SECURITY, | ||
| moduleNameTrivy: ModuleNameMap.SECURITY_TRIVY, | ||
| }, | ||
| { | ||
| title: 'Bulk Edit', | ||
|
|
@@ -181,18 +182,19 @@ export default class Navigation extends Component< | |
| componentDidUpdate(prevProps) { | ||
| if ( | ||
| this.props.moduleInInstallingState !== prevProps.moduleInInstallingState && | ||
| this.props.moduleInInstallingState === ModuleNameMap.SECURITY | ||
| (this.props.moduleInInstallingState === ModuleNameMap.SECURITY || ModuleNameMap.SECURITY_TRIVY) | ||
|
||
| ) { | ||
| this.getSecurityModuleStatus(MODULE_STATUS_RETRY_COUNT) | ||
| } | ||
| } | ||
|
|
||
| async getSecurityModuleStatus(retryOnError: number): Promise<void> { | ||
| if (this.props.installedModuleMap.current?.[ModuleNameMap.SECURITY] || window._env_.K8S_CLIENT) { | ||
| if (this.props.installedModuleMap.current?.[ModuleNameMap.SECURITY] || window._env_.K8S_CLIENT || this.props.installedModuleMap.current?.[ModuleNameMap.SECURITY_TRIVY]) { | ||
| return | ||
| } | ||
| try { | ||
| const { result } = await getModuleInfo(ModuleNameMap.SECURITY) | ||
| const { result:result2 } = await getModuleInfo(ModuleNameMap.SECURITY_TRIVY) | ||
|
||
| if (result?.status === ModuleStatus.INSTALLED) { | ||
| this.props.installedModuleMap.current = { | ||
| ...this.props.installedModuleMap.current, | ||
|
|
@@ -204,6 +206,19 @@ export default class Navigation extends Component< | |
| this.getSecurityModuleStatus(MODULE_STATUS_RETRY_COUNT) | ||
| }, MODULE_STATUS_POLLING_INTERVAL) | ||
| } | ||
| if (result2?.status === ModuleStatus.INSTALLED) { | ||
| this.props.installedModuleMap.current = { | ||
| ...this.props.installedModuleMap.current, | ||
| [ModuleNameMap.SECURITY_TRIVY]: true, | ||
| } | ||
| this.setState({ forceUpdateTime: Date.now() }) | ||
| } else if (result2?.status === ModuleStatus.INSTALLING) { | ||
| this.securityModuleStatusTimer = setTimeout(() => { | ||
|
||
| this.getSecurityModuleStatus(MODULE_STATUS_RETRY_COUNT) | ||
| }, MODULE_STATUS_POLLING_INTERVAL) | ||
| } | ||
|
|
||
|
|
||
| } catch (error) { | ||
| if (retryOnError >= 0) { | ||
| this.getSecurityModuleStatus(retryOnError--) | ||
|
|
@@ -301,7 +316,8 @@ export default class Navigation extends Component< | |
| return ( | ||
| (this.props.serverMode === SERVER_MODE.FULL && !item.moduleName) || | ||
| (this.props.serverMode === SERVER_MODE.EA_ONLY && item.isAvailableInEA) || | ||
| this.props.installedModuleMap.current?.[item.moduleName] | ||
| (this.props.installedModuleMap.current?.[item.moduleName]) || | ||
| (this.props.installedModuleMap.current?.[item.moduleNameTrivy]) | ||
| ) | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
trivySecurityInfo fix camel casing