From 04ca8fea3766223a2859100ee3c30dcb8f4a048e Mon Sep 17 00:00:00 2001 From: shivani170 Date: Wed, 25 Mar 2026 09:49:04 +0530 Subject: [PATCH 01/38] wip: security recommendation modal --- package.json | 2 +- src/components/CIPipelineN/Build.tsx | 83 ++++++---- src/components/CIPipelineN/CIPipeline.tsx | 5 +- .../app/details/appDetails/AppSecurity.tsx | 28 +++- .../app/details/appDetails/appDetails.type.ts | 8 + .../app/details/cIDetails/CIDetails.tsx | 7 +- .../SecrityScanRecommendationBar.tsx | 71 ++++++++ .../SecurityRecommendation.utils.tsx | 143 ++++++++++++++++ .../SecurityScanModal.tsx | 156 ++++++++++++++++++ ...SecurityScanRecommendations.components.tsx | 78 +++++++++ .../SecurityScanRecommendation/security.scss | 113 +++++++++++++ .../SecurityScanRecommendation/types.ts | 49 ++++++ .../ciPipeline/ciPipeline.service.ts | 3 + src/components/ciPipeline/types.ts | 1 + yarn.lock | 10 +- 15 files changed, 712 insertions(+), 45 deletions(-) create mode 100644 src/components/app/details/cIDetails/SecurityScanRecommendation/SecrityScanRecommendationBar.tsx create mode 100644 src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx create mode 100644 src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanModal.tsx create mode 100644 src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanRecommendations.components.tsx create mode 100644 src/components/app/details/cIDetails/SecurityScanRecommendation/security.scss create mode 100644 src/components/app/details/cIDetails/SecurityScanRecommendation/types.ts diff --git a/package.json b/package.json index e41666b00b..24f731d1ae 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "1.23.3", + "@devtron-labs/devtron-fe-common-lib": "1.23.3-beta-9", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rjsf/core": "^5.13.3", "@rjsf/utils": "^5.13.3", diff --git a/src/components/CIPipelineN/Build.tsx b/src/components/CIPipelineN/Build.tsx index bc951302ff..c79d79ee4c 100644 --- a/src/components/CIPipelineN/Build.tsx +++ b/src/components/CIPipelineN/Build.tsx @@ -166,6 +166,12 @@ export const Build = ({ setFormData(_formData) } + const handleDockerfileScanToggle = (): void => { + const _formData = { ...formData } + _formData.dockerfileScanEnabled = !_formData.dockerfileScanEnabled + setFormData(_formData) + } + const renderBasicCI = () => { const _webhookData: WebhookCIProps = { webhookConditionList: formData.webhookConditionList, @@ -206,45 +212,54 @@ export const Build = ({ const renderPipelineName = () => { return ( - + ) } const renderScanner = () => ( - <> -
-
-
- -
-

Scan for vulnerabilities

-

Perform security scan after container image is built.

-
- +
+
+ +
+

Scan for vulnerabilities

+

Perform security scan after container image is built.

+
- +
+
+ +
+

Scan for recommendations

+

+ Perform linting scan of your docker file and get recommended optimizations. +

+
+ +
+
) return pageState === ViewType.LOADING.toString() ? ( @@ -252,7 +267,7 @@ export const Build = ({
) : ( -
+
{renderBasicCI()} {!isJobView && isAdvanced && ( <> diff --git a/src/components/CIPipelineN/CIPipeline.tsx b/src/components/CIPipelineN/CIPipeline.tsx index 17e1da1640..33e152b6dc 100644 --- a/src/components/CIPipelineN/CIPipeline.tsx +++ b/src/components/CIPipelineN/CIPipeline.tsx @@ -128,6 +128,7 @@ export default function CIPipeline({ materials: [], triggerType: window._env_.DEFAULT_CI_TRIGGER_TYPE_MANUAL ? TriggerType.Manual : TriggerType.Auto, scanEnabled: false, + dockerfileScanEnabled: false, gitHost: undefined, webhookEvents: [], ciPipelineSourceTypeOptions: [], @@ -616,7 +617,8 @@ export default function CIPipeline({ validateStage(BuildStageVariable.Build, formData) validateStage(BuildStageVariable.PostBuild, formData) const scanValidation = - isJobCard || !isSecurityModuleInstalled || formData.scanEnabled || !window._env_.FORCE_SECURITY_SCANNING + isJobCard || !isSecurityModuleInstalled || formData.scanEnabled || formData.dockerfileScanEnabled || + !window._env_.FORCE_SECURITY_SCANNING if (!scanValidation) { setApiInProgress(false) ToastManager.showToast({ @@ -679,6 +681,7 @@ export default function CIPipeline({ ...formData, materials: _materials, scanEnabled: !isJobCard && isSecurityModuleInstalled ? formData.scanEnabled : false, + dockerfileScanEnabled: !isJobCard && isSecurityModuleInstalled ? formData.dockerfileScanEnabled : false, }, _ciPipeline, _materials, diff --git a/src/components/app/details/appDetails/AppSecurity.tsx b/src/components/app/details/appDetails/AppSecurity.tsx index 8afe0d8d3d..7aadac433e 100644 --- a/src/components/app/details/appDetails/AppSecurity.tsx +++ b/src/components/app/details/appDetails/AppSecurity.tsx @@ -14,9 +14,13 @@ * limitations under the License. */ -import { getSecurityScan, useAsync } from '@devtron-labs/devtron-fe-common-lib' +import { getSecurityScan, getSecurityScanRecommendations, useAsync } from '@devtron-labs/devtron-fe-common-lib' -import { UseGetAppSecurityDetailsProps, UseGetAppSecurityDetailsReturnType } from './appDetails.type' +import { + UseGetAppSecurityDetailsProps, + UseGetAppSecurityDetailsReturnType, + UseSecurityRecommendationReturnType, +} from './appDetails.type' export const useGetAppSecurityDetails = ({ appId, @@ -26,7 +30,7 @@ export const useGetAppSecurityDetails = ({ }: UseGetAppSecurityDetailsProps): UseGetAppSecurityDetailsReturnType => { const [scanResultLoading, scanResultResponse, scanResultError, reloadScanResult] = useAsync( () => getSecurityScan({ appId, envId, artifactId, installedAppId }), - [appId, envId, installedAppId], + [appId, envId, artifactId, installedAppId], !!appId || !!installedAppId, ) @@ -37,3 +41,21 @@ export const useGetAppSecurityDetails = ({ reloadScanResult, } } + +export const useGetAppSecurityDetailsRecommendations = ({ + appId, + buildId, +}: UseGetAppSecurityDetailsProps): UseSecurityRecommendationReturnType => { + const [scanResultLoading, scanResultResponse, scanResultError, reloadScanResult] = useAsync( + () => getSecurityScanRecommendations({ appId, buildId }), + [appId, buildId], + !!appId && !!buildId, + ) + + return { + scanResultLoading, + scanResultResponse, + scanResultError, + reloadScanResult, + } +} diff --git a/src/components/app/details/appDetails/appDetails.type.ts b/src/components/app/details/appDetails/appDetails.type.ts index 7fec8fdde8..15dd50b690 100644 --- a/src/components/app/details/appDetails/appDetails.type.ts +++ b/src/components/app/details/appDetails/appDetails.type.ts @@ -24,6 +24,7 @@ import { EnvAppsMetaDTO, OptionType, ResponseType, + ScanRecommendationsDTO, ScanResultDTO, SelectPickerProps, ServerErrors, @@ -182,6 +183,7 @@ export interface UseGetAppSecurityDetailsProps { envId?: number installedAppId?: number artifactId?: number + buildId?: number } export interface UseGetAppSecurityDetailsReturnType { scanResultLoading: boolean @@ -189,6 +191,12 @@ export interface UseGetAppSecurityDetailsReturnType { scanResultError: ServerErrors reloadScanResult: () => void } +export interface UseSecurityRecommendationReturnType { + scanResultLoading: boolean + scanResultResponse: ResponseType + scanResultError: ServerErrors + reloadScanResult: () => void +} export enum HibernationModalTypes { HIBERNATE = 'hibernate', diff --git a/src/components/app/details/cIDetails/CIDetails.tsx b/src/components/app/details/cIDetails/CIDetails.tsx index 4bb7fd4f71..dee719a0e9 100644 --- a/src/components/app/details/cIDetails/CIDetails.tsx +++ b/src/components/app/details/cIDetails/CIDetails.tsx @@ -44,6 +44,7 @@ import { ErrorScreenManager, SecurityDetailsCards, sanitizeTargetPlatforms, + useMainContext, } from '@devtron-labs/devtron-fe-common-lib' import { Switch, Route, Redirect, useRouteMatch, useParams, useHistory, generatePath } from 'react-router-dom' import { @@ -64,6 +65,7 @@ import { CIPipelineBuildType } from '../../../ciPipeline/types' import { renderCIListHeader, renderDeploymentHistoryTriggerMetaText } from '../cdDetails/utils' import { importComponentFromFELibrary } from '@Components/common' import { useGetAppSecurityDetails } from '../appDetails/AppSecurity' +import { SecurityScansRecommendations } from './SecurityScanRecommendation/SecurityScanRecommendations.components' const SecurityModalSidebar = importComponentFromFELibrary('SecurityModalSidebar', null, 'function') const terminalStatus = new Set(['succeeded', 'failed', 'error', 'cancelled', 'nottriggered', 'notbuilt']) @@ -650,7 +652,9 @@ const HistoryLogs = ({ } const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTabType) => { - const { appId } = useParams<{ appId: string }>() + const { appId, buildId } = useParams<{ appId: string; buildId: string }>() + const { isEnterprise } = useMainContext() + const computedAppId = appId ?? appIdFromParent @@ -688,6 +692,7 @@ const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTabType) = return (
+ {isEnterprise && }
) diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecrityScanRecommendationBar.tsx b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecrityScanRecommendationBar.tsx new file mode 100644 index 0000000000..536d1bafc3 --- /dev/null +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecrityScanRecommendationBar.tsx @@ -0,0 +1,71 @@ +import dayjs from 'dayjs' + +import { DATE_TIME_FORMATS, Icon, SegmentedBarChart, ZERO_TIME_STRING } from '@devtron-labs/devtron-fe-common-lib' + +import { SecurityScanRecommendationBarProps } from './types' + +export const SecurityScanRecommendationBar = ({ + summary, + hasRecommendations, + handleSecurityScanModal, + isModalView = false, + lastScanTime, +}: SecurityScanRecommendationBarProps) => { + const { error, warning } = summary + const totalCount = error + warning + const hasThreats = error || warning + const shouldShowLastScanTime = + isModalView && !!lastScanTime && lastScanTime !== ZERO_TIME_STRING && lastScanTime !== 0 + + return ( +
+ {!hasThreats || !hasRecommendations ? ( + <> +
+ Dockerfile Best Practices + Looks good! +
+ +
+
+ No recommendations suggested +
+ + ) : ( +
+
+
+ {!isModalView && Dockerfile Best Practices} + {totalCount} Recommendations +
+
+ +
+
+ {shouldShowLastScanTime ? ( +
+ + + Scanned on {dayjs(lastScanTime).format(DATE_TIME_FORMATS.TWELVE_HOURS_FORMAT)}{' '} + +
+ ) : ( + '' + )} +
+ )} +
+ ) +} diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx new file mode 100644 index 0000000000..1cbdc47440 --- /dev/null +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx @@ -0,0 +1,143 @@ +import { useEffect } from 'react' + +import { FiltersTypeEnum, stopPropagation, TableCellComponentProps } from '@devtron-labs/devtron-fe-common-lib' + +import { + RecommendationResult, + RecommendationSnippetLine, + SecurityScanRecommendationRowTypes, + SecurityScanRecommendationTableAdditionalProps, +} from './types' + +export const getRecommendationRowId = (recommendation: RecommendationResult, index: number) => + `recommendation-${recommendation.code || 'code'}-${index}` + +export const getSnippetLines = ({ + codeSnippet, +}: Pick): RecommendationSnippetLine[] => + [...(codeSnippet?.before || []), codeSnippet?.current, ...(codeSnippet?.after || [])].filter(Boolean) + +export const RecommendationCellComponent = ({ + row, + isExpandedRow, + expandRowCallback, + registerExpandRowCallback, +}: TableCellComponentProps< + SecurityScanRecommendationRowTypes, + FiltersTypeEnum.URL, + SecurityScanRecommendationTableAdditionalProps +>) => { + useEffect(() => { + if (isExpandedRow) { + return undefined + } + + registerExpandRowCallback(row.id, expandRowCallback) + + return () => { + registerExpandRowCallback(row.id) + } + }, [expandRowCallback, isExpandedRow, registerExpandRowCallback, row.id]) + + if (!isExpandedRow) { + return
{row.data.title}
+ } + + const snippetLines = getSnippetLines(row.data) + + return ( +
+
Dockerfile
+ {snippetLines.length ? ( +
+ {snippetLines.map((snippetLine) => ( +
+ + {snippetLine.line || ''} + + {snippetLine.content} +
+ ))} +
+ ) : ( +
No code snippet available.
+ )} +
+ ) +} + +export const CodeCellComponent = ({ + row, + isExpandedRow, +}: TableCellComponentProps< + SecurityScanRecommendationRowTypes, + FiltersTypeEnum.URL, + SecurityScanRecommendationTableAdditionalProps +>) => { + if (isExpandedRow) { + return null + } + + return ( + + {row.data.code} + + ) +} + +export const LevelCellComponent = ({ + row, + isExpandedRow, +}: TableCellComponentProps< + SecurityScanRecommendationRowTypes, + FiltersTypeEnum.URL, + SecurityScanRecommendationTableAdditionalProps +>) => { + if (isExpandedRow) { + return null + } + + return ( +
+ + {row.data.level} + +
+ ) +} + +export const SECURITY_SCAN_RECOMMENDATIONS_TABLE_COLUMNS = [ + { + label: 'Recommendations', + field: 'title', + size: null, + CellComponent: RecommendationCellComponent, + }, + { + label: 'Level', + field: 'level', + size: { fixed: 100 }, + isSortable: true, + CellComponent: LevelCellComponent, + }, + { + label: 'Code', + field: 'code', + size: { fixed: 150 }, + isSortable: true, + CellComponent: CodeCellComponent, + }, +] diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanModal.tsx b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanModal.tsx new file mode 100644 index 0000000000..ea0ca15827 --- /dev/null +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanModal.tsx @@ -0,0 +1,156 @@ +import { useCallback, useMemo, useRef } from 'react' + +import { + Button, + ButtonStyleType, + ButtonVariantType, + ComponentSizeType, + FiltersTypeEnum, + Icon, + PaginationEnum, + stopPropagation, + Table, + VisibleModal, +} from '@devtron-labs/devtron-fe-common-lib' + +import { SecurityScanRecommendationBar } from './SecrityScanRecommendationBar' +import { getRecommendationRowId, SECURITY_SCAN_RECOMMENDATIONS_TABLE_COLUMNS } from './SecurityRecommendation.utils' +import { + ExpandRowCallback, + SecurityScanRecommendationModalProps, + SecurityScanRecommendationRowTypes, + SecurityScanRecommendationTableAdditionalProps, +} from './types' + +import './security.scss' + +const CLOSE_BUTTON_ID = 'security-scan-recommendations-close' + +export const SecurityScanModal = ({ + summary, + hasRecommendations, + recommendations, + handleSecurityScanModal, + lastScanTime, +}: SecurityScanRecommendationModalProps) => { + const expandRowCallbacksRef = useRef>({}) + + const registerExpandRowCallback = useCallback< + SecurityScanRecommendationTableAdditionalProps['registerExpandRowCallback'] + >((rowId, expandRowCallback) => { + if (!expandRowCallback) { + delete expandRowCallbacksRef.current[rowId] + + return + } + + expandRowCallbacksRef.current[rowId] = expandRowCallback + }, []) + + const onRowClick = useCallback( + (row: { id: string; data: SecurityScanRecommendationRowTypes }, isExpandedRow: boolean) => { + const rowId = isExpandedRow ? row.data.parentRowId : row.id + + expandRowCallbacksRef.current[rowId]?.({ + stopPropagation: () => {}, + } as Parameters[0]) + }, + [], + ) + + const rows = useMemo( + () => + recommendations.map((recommendation, index) => { + const rowId = getRecommendationRowId(recommendation, index) + const rowData: SecurityScanRecommendationRowTypes = { + id: rowId, + parentRowId: rowId, + code: recommendation.code, + title: recommendation.title, + level: recommendation.level, + message: recommendation.message, + file: recommendation.file, + line: recommendation.line, + documentationUrl: recommendation.documentationUrl, + codeSnippet: recommendation.codeSnippet, + } + + const expandedRowId = `expanded-row-${rowId}` as const + + return { + id: rowId, + data: rowData, + expandableRows: [ + { + id: expandedRowId, + data: { + ...rowData, + id: expandedRowId, + }, + }, + ], + } + }), + [recommendations], + ) + + const additionalProps = useMemo( + () => ({ + registerExpandRowCallback, + }), + [registerExpandRowCallback], + ) + + return ( + +
+
+

Dockerfile Linting

+
+ + + id="table__security-scan-recommendations" + columns={SECURITY_SCAN_RECOMMENDATIONS_TABLE_COLUMNS} + rows={rows} + filtersVariant={FiltersTypeEnum.URL} + paginationVariant={PaginationEnum.NOT_PAGINATED} + filter={null} + additionalProps={additionalProps} + emptyStateConfig={{ + noRowsConfig: { + title: 'No recommendations found', + }, + }} + onRowClick={onRowClick} + /> +
+
+ ) +} diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanRecommendations.components.tsx b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanRecommendations.components.tsx new file mode 100644 index 0000000000..d10c747831 --- /dev/null +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanRecommendations.components.tsx @@ -0,0 +1,78 @@ +import { useState } from 'react' + +import { ErrorScreenManager, Progressing, ScanRecommendationsDTO } from '@devtron-labs/devtron-fe-common-lib' + +import { useGetAppSecurityDetailsRecommendations } from '../../appDetails/AppSecurity' +import { SecurityScanRecommendationBar } from './SecrityScanRecommendationBar' +import { SecurityScanModal } from './SecurityScanModal' +import { SecurityScansRecommendationsProps } from './types' + +export const SecurityScansRecommendations = ({ appId, buildId }: SecurityScansRecommendationsProps) => { + const { scanResultLoading, scanResultResponse, scanResultError, reloadScanResult } = + useGetAppSecurityDetailsRecommendations({ + appId, + buildId, + }) + const [showSecurityScanModal, setShowSecurityScanModal] = useState(false) + + const recommendations: ScanRecommendationsDTO = scanResultResponse?.result + + if (!appId || !buildId) { + return null + } + + if (scanResultLoading) { + return ( +
+
+ +
+
+ ) + } + + if (scanResultError) { + return + } + + if (!recommendations) { + return null + } + + const { results, severity_summary: severitySummary } = recommendations + const hasRecommendations = !!results?.length + + const handleSecurityScanModal = () => { + setShowSecurityScanModal((currentState) => !currentState) + } + + return ( +
+ {!hasRecommendations ? ( +
+ + No recommendations available for this scan. + +
+ ) : ( +
+ + {showSecurityScanModal && ( + + )} +
+ )} +
+ ) +} diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/security.scss b/src/components/app/details/cIDetails/SecurityScanRecommendation/security.scss new file mode 100644 index 0000000000..48b66a4cf5 --- /dev/null +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/security.scss @@ -0,0 +1,113 @@ +.warning { + color: var(--Y500); + border: 1px solid var(--progress-O200, #FFBEAD); + background: var(--progress-O100, #FFE5DE); + color: var(--O600, var(--progress-O600, #E55E39)); +} + +.error { + color: var(--R500); +} + +.security-scan-modal { + &__title { + color: var(--N900); + word-break: break-word; + } + + &__code-link { + color: var(--B500); + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } + + &__expanded-row { + background: var(--bg-primary); + min-width: calc(300% + 32px); + border: 1px solid var(--N200); + border-radius: 4px; + overflow: hidden; + } + + &__expanded-header { + display: flex; + justify-content: space-between; + gap: 12px; + background: var(--bg-secondary); + } + + &__expanded-title { + color: var(--N900); + font-size: 13px; + font-weight: 600; + line-height: 20px; + } + + &__snippet-empty { + color: var(--N500); + font-size: 12px; + line-height: 16px; + } + + &__snippet { + overflow: hidden; + background: var(--bg-primary); + } + + &__snippet-empty { + padding: 12px; + border: 1px dashed var(--N200); + border-radius: 8px; + background: var(--bg-primary); + } + + &__snippet-line { + display: grid; + grid-template-columns: 24px minmax(0, 1fr); + gap: 12px; + padding: 8px 12px; + + &--issue { + color: var(--R500); + } + } + + &__snippet-content { + color: var(--N900); + white-space: pre-wrap; + word-break: break-word; + } + + .generic-table__row--is-expanded, + .generic-table__row--is-expanded > .generic-table__cell { + background-color: var(--bg-secondary); + } + + .generic-table__row--is-expanded:hover, + .generic-table__row--is-expanded:hover > .generic-table__cell, + .generic-table__row--is-expanded.generic-table__row--active, + .generic-table__row--is-expanded.generic-table__row--active > .generic-table__cell { + background-color: var(--bg-secondary); + } + + .generic-table__row--expanded-row { + background-color: transparent; + } + + .generic-table__row--expanded-row > .generic-table__cell, + .generic-table__row--expanded-row > .expanded-tree-line { + background-color: transparent; + } + + .generic-table__row--expanded-row:hover, + .generic-table__row--expanded-row:hover > .generic-table__cell, + .generic-table__row--expanded-row:hover > .expanded-tree-line, + .generic-table__row--expanded-row.generic-table__row--active, + .generic-table__row--expanded-row.generic-table__row--active > .generic-table__cell, + .generic-table__row--expanded-row.generic-table__row--active > .expanded-tree-line { + background-color: transparent; + } +} \ No newline at end of file diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/types.ts b/src/components/app/details/cIDetails/SecurityScanRecommendation/types.ts new file mode 100644 index 0000000000..0ed317ed5b --- /dev/null +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/types.ts @@ -0,0 +1,49 @@ +import type { MouseEvent } from 'react' + +import { ScanRecommendationsDTO } from '@devtron-labs/devtron-fe-common-lib' + +export interface SecurityScansRecommendationsProps { + appId: number + buildId: number +} + +export type RecommendationResult = ScanRecommendationsDTO['results'][number] + +export type RecommendationSnippetLine = { + line?: number + content: string + isIssue?: boolean +} + +export type SecurityScanRecommendationModalProps = { + summary: ScanRecommendationsDTO['severity_summary'] + hasRecommendations: boolean + recommendations: ScanRecommendationsDTO['results'] + handleSecurityScanModal?: () => void + lastScanTime?: ScanRecommendationsDTO['createdOn'] | string + isModalView?: boolean +} + +export type SecurityScanRecommendationBarProps = Pick< + SecurityScanRecommendationModalProps, + 'hasRecommendations' | 'summary' | 'handleSecurityScanModal' | 'isModalView' | 'lastScanTime' +> & {} + +export type SecurityScanRecommendationRowTypes = { + id: string + parentRowId: string + code: string + title: string + level: string + message: string + file: string + line: number + documentationUrl: string + codeSnippet: RecommendationResult['codeSnippet'] +} + +export type ExpandRowCallback = (event: MouseEvent) => void + +export type SecurityScanRecommendationTableAdditionalProps = { + registerExpandRowCallback: (rowId: string, expandRowCallback?: ExpandRowCallback) => void +} diff --git a/src/components/ciPipeline/ciPipeline.service.ts b/src/components/ciPipeline/ciPipeline.service.ts index 0e1175d9d0..89d4efb225 100644 --- a/src/components/ciPipeline/ciPipeline.service.ts +++ b/src/components/ciPipeline/ciPipeline.service.ts @@ -102,6 +102,7 @@ export function getInitData( preBuildStage: emptyStepsData(), postBuildStage: emptyStepsData(), scanEnabled, + dockerfileScanEnabled: true, ciPipelineEditable: true, workflowCacheConfig: pipelineMetaConfig.result.workflowCacheConfig ?? null, }, @@ -420,6 +421,7 @@ function createCIPatchRequest(ciPipeline, formData, isExternalCI: boolean, webho preBuildStage, postBuildStage, scanEnabled: formData.scanEnabled, + dockerfileScanEnabled: formData.dockerfileScanEnabled, dockerArgs: formData.args .filter((arg) => arg.key && arg.key.length && arg.value && arg.value.length) .reduce((agg, curr) => { @@ -621,6 +623,7 @@ function parseCIResponse( args: args.length ? args : [], externalCiConfig: createCurlRequest(ciPipeline.externalCiConfig), scanEnabled: ciPipeline.scanEnabled, + dockerfileScanEnabled: ciPipeline.dockerfileScanEnabled, gitHost, webhookEvents, ciPipelineSourceTypeOptions, diff --git a/src/components/ciPipeline/types.ts b/src/components/ciPipeline/types.ts index 5c82c4b020..85bec67beb 100644 --- a/src/components/ciPipeline/types.ts +++ b/src/components/ciPipeline/types.ts @@ -295,6 +295,7 @@ export interface CIPipelineDataType { name: string linkedCount: number scanEnabled?: boolean + dockerfileScanEnabled?: boolean isDockerConfigOverridden?: boolean dockerConfigOverride?: DockerConfigOverrideType environmentId?: any diff --git a/yarn.lock b/yarn.lock index 032b7deb56..c1519c5f22 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1729,9 +1729,9 @@ __metadata: languageName: node linkType: hard -"@devtron-labs/devtron-fe-common-lib@npm:1.23.3": - version: 1.23.3 - resolution: "@devtron-labs/devtron-fe-common-lib@npm:1.23.3" +"@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-9": + version: 1.23.3-beta-9 + resolution: "@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-9" dependencies: "@codemirror/autocomplete": "npm:6.18.6" "@codemirror/lang-json": "npm:6.0.1" @@ -1785,7 +1785,7 @@ __metadata: react-select: 5.8.0 rxjs: ^7.8.1 yaml: ^2.4.1 - checksum: 10c0/16248f937348ef4a82bf877a978a983235c77becc7e910dee070bc76f7026ed52cc4d3a97f99075ff660a2c95567821f0044d60b933a757453d3d35cfa9510f5 + checksum: 10c0/6d3c86a4491bed9273f5bc8e216327ef114f2f537237ad851cc04124506c4fdc24c15a4d469f97d627e69b309599801f19fd58ac5491a8fac4123afb3cf07198 languageName: node linkType: hard @@ -5599,7 +5599,7 @@ __metadata: version: 0.0.0-use.local resolution: "dashboard@workspace:." dependencies: - "@devtron-labs/devtron-fe-common-lib": "npm:1.23.3" + "@devtron-labs/devtron-fe-common-lib": "npm:1.23.3-beta-9" "@esbuild-plugins/node-globals-polyfill": "npm:0.2.3" "@playwright/test": "npm:^1.32.1" "@rjsf/core": "npm:^5.13.3" From 95dac0f827e269ca157607e4c0ce51e0eae29755 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Wed, 25 Mar 2026 10:35:29 +0530 Subject: [PATCH 02/38] chore: code refactoring --- .../app/details/cIDetails/CIDetails.tsx | 57 ++++++++++--------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/src/components/app/details/cIDetails/CIDetails.tsx b/src/components/app/details/cIDetails/CIDetails.tsx index dee719a0e9..4bdc6a71d6 100644 --- a/src/components/app/details/cIDetails/CIDetails.tsx +++ b/src/components/app/details/cIDetails/CIDetails.tsx @@ -221,7 +221,9 @@ export default function CIDetails({ isJobView, filteredEnvIds }: { isJobView?: b const pipeline = pipelinesMap.get(+pipelineId) const redirectToArtifactLogs = () => { - push(`${URLS.APPLICATION_MANAGEMENT_APP}/${pipeline.parentAppId}/${URLS.APP_CI_DETAILS}/${pipeline.parentCiPipeline}/logs`) + push( + `${URLS.APPLICATION_MANAGEMENT_APP}/${pipeline.parentAppId}/${URLS.APP_CI_DETAILS}/${pipeline.parentCiPipeline}/logs`, + ) } const renderSourcePipelineButton = () => { return ( @@ -655,7 +657,6 @@ const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTabType) = const { appId, buildId } = useParams<{ appId: string; buildId: string }>() const { isEnterprise } = useMainContext() - const computedAppId = appId ?? appIdFromParent const { scanResultLoading, scanResultResponse, scanResultError, reloadScanResult } = useGetAppSecurityDetails({ @@ -663,37 +664,41 @@ const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTabType) = artifactId, }) - if (['starting', 'running'].includes(status.toLowerCase())) { - return - } + const renderSecurityDetailsCards = () => { + if (['starting', 'running'].includes(status.toLowerCase())) { + return + } - if (!artifactId) { - return ( - - ) - } + if (!artifactId) { + return ( + + ) + } - if (scanResultLoading) { - return ( -
- -
- ) - } - if (scanResultError) { - return - } - if (!scanResultResponse?.result.scanned) { - return + if (scanResultLoading) { + return ( +
+ +
+ ) + } + if (scanResultError) { + return + } + if (!scanResultResponse?.result.scanned) { + return + } + + return } return (
{isEnterprise && } - + {renderSecurityDetailsCards()}
) } From 61119411f045915c6bd308fa29ed04391b691555 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Wed, 25 Mar 2026 11:37:16 +0530 Subject: [PATCH 03/38] chore: css fixes --- .../cIDetails/SecurityScanRecommendation/SecurityScanModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanModal.tsx b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanModal.tsx index ea0ca15827..f0ab0f55f5 100644 --- a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanModal.tsx +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanModal.tsx @@ -103,7 +103,7 @@ export const SecurityScanModal = ({ return ( Date: Wed, 25 Mar 2026 11:52:28 +0530 Subject: [PATCH 04/38] chore: scroll fix --- .../SecurityRecommendation.utils.tsx | 30 +++++----- .../SecurityScanModal.tsx | 56 ++++++++++--------- .../SecurityScanRecommendation/security.scss | 14 ++++- 3 files changed, 56 insertions(+), 44 deletions(-) diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx index 1cbdc47440..2cd6335210 100644 --- a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx @@ -50,21 +50,23 @@ export const RecommendationCellComponent = ({
Dockerfile
{snippetLines.length ? (
- {snippetLines.map((snippetLine) => ( -
- + {snippetLines.map((snippetLine) => ( +
- {snippetLine.line || ''} - - {snippetLine.content} -
- ))} + + {snippetLine.line || ''} + + {snippetLine.content} +
+ ))} +
) : (
No code snippet available.
diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanModal.tsx b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanModal.tsx index f0ab0f55f5..ba028b1715 100644 --- a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanModal.tsx +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanModal.tsx @@ -103,13 +103,13 @@ export const SecurityScanModal = ({ return (
@@ -125,31 +125,33 @@ export const SecurityScanModal = ({ icon={} />
- - - id="table__security-scan-recommendations" - columns={SECURITY_SCAN_RECOMMENDATIONS_TABLE_COLUMNS} - rows={rows} - filtersVariant={FiltersTypeEnum.URL} - paginationVariant={PaginationEnum.NOT_PAGINATED} - filter={null} - additionalProps={additionalProps} - emptyStateConfig={{ - noRowsConfig: { - title: 'No recommendations found', - }, - }} - onRowClick={onRowClick} - /> +
+ + + id="table__security-scan-recommendations" + columns={SECURITY_SCAN_RECOMMENDATIONS_TABLE_COLUMNS} + rows={rows} + filtersVariant={FiltersTypeEnum.URL} + paginationVariant={PaginationEnum.NOT_PAGINATED} + filter={null} + additionalProps={additionalProps} + emptyStateConfig={{ + noRowsConfig: { + title: 'No recommendations found', + }, + }} + onRowClick={onRowClick} + /> +
) diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/security.scss b/src/components/app/details/cIDetails/SecurityScanRecommendation/security.scss index 48b66a4cf5..62f17dcf08 100644 --- a/src/components/app/details/cIDetails/SecurityScanRecommendation/security.scss +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/security.scss @@ -57,6 +57,11 @@ background: var(--bg-primary); } + &__snippet-lines { + min-width: 100%; + width: max-content; + } + &__snippet-empty { padding: 12px; border: 1px dashed var(--N200); @@ -66,9 +71,11 @@ &__snippet-line { display: grid; - grid-template-columns: 24px minmax(0, 1fr); + grid-template-columns: minmax(24px, max-content) max-content; gap: 12px; padding: 8px 12px; + min-width: 100%; + width: max-content; &--issue { color: var(--R500); @@ -77,8 +84,9 @@ &__snippet-content { color: var(--N900); - white-space: pre-wrap; - word-break: break-word; + white-space: pre; + word-break: normal; + overflow-wrap: normal; } .generic-table__row--is-expanded, From 014006e4b245acc32a5817612a9d8b90a8bf3d3a Mon Sep 17 00:00:00 2001 From: shivani170 Date: Thu, 26 Mar 2026 07:41:17 +0530 Subject: [PATCH 05/38] chore: hadolint tool added in the security --- src/components/app/details/cIDetails/CIDetails.tsx | 2 +- .../SecurityScanRecommendations.components.tsx | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/components/app/details/cIDetails/CIDetails.tsx b/src/components/app/details/cIDetails/CIDetails.tsx index 4bdc6a71d6..b49f468a16 100644 --- a/src/components/app/details/cIDetails/CIDetails.tsx +++ b/src/components/app/details/cIDetails/CIDetails.tsx @@ -696,7 +696,7 @@ const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTabType) = } return ( -
+
{isEnterprise && } {renderSecurityDetailsCards()}
diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanRecommendations.components.tsx b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanRecommendations.components.tsx index d10c747831..2c66f24766 100644 --- a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanRecommendations.components.tsx +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanRecommendations.components.tsx @@ -1,6 +1,11 @@ import { useState } from 'react' -import { ErrorScreenManager, Progressing, ScanRecommendationsDTO } from '@devtron-labs/devtron-fe-common-lib' +import { + ErrorScreenManager, + Progressing, + ScannedByToolModal, + ScanRecommendationsDTO, +} from '@devtron-labs/devtron-fe-common-lib' import { useGetAppSecurityDetailsRecommendations } from '../../appDetails/AppSecurity' import { SecurityScanRecommendationBar } from './SecrityScanRecommendationBar' @@ -56,6 +61,13 @@ export const SecurityScansRecommendations = ({ appId, buildId }: SecurityScansRe
) : (
+
+

Dockerfile Linting

+ +
Date: Thu, 26 Mar 2026 11:15:32 +0530 Subject: [PATCH 06/38] chore: css fixes --- .../app/details/appDetails/AppSecurity.tsx | 19 ++++----- .../app/details/appDetails/appDetails.type.ts | 35 +++++++++++++++-- .../app/details/cIDetails/CIDetails.tsx | 23 +++++++++-- .../SecrityScanRecommendationBar.tsx | 22 ++++++++--- .../SecurityRecommendation.utils.tsx | 31 ++++++++++++++- ...SecurityScanRecommendations.components.tsx | 39 ++++++++----------- .../SecurityScanRecommendation/types.ts | 8 ++-- 7 files changed, 128 insertions(+), 49 deletions(-) diff --git a/src/components/app/details/appDetails/AppSecurity.tsx b/src/components/app/details/appDetails/AppSecurity.tsx index 7aadac433e..5a87cba932 100644 --- a/src/components/app/details/appDetails/AppSecurity.tsx +++ b/src/components/app/details/appDetails/AppSecurity.tsx @@ -46,16 +46,17 @@ export const useGetAppSecurityDetailsRecommendations = ({ appId, buildId, }: UseGetAppSecurityDetailsProps): UseSecurityRecommendationReturnType => { - const [scanResultLoading, scanResultResponse, scanResultError, reloadScanResult] = useAsync( - () => getSecurityScanRecommendations({ appId, buildId }), - [appId, buildId], - !!appId && !!buildId, - ) + const [ + scanRecommendationsResultLoading, + scanRecommendationsResultResponse, + scanRecommendationsResultError, + reloadScanRecommendationsResult, + ] = useAsync(() => getSecurityScanRecommendations({ appId, buildId }), [appId, buildId], !!appId && !!buildId) return { - scanResultLoading, - scanResultResponse, - scanResultError, - reloadScanResult, + scanRecommendationsResultLoading, + scanRecommendationsResultResponse, + scanRecommendationsResultError, + reloadScanRecommendationsResult, } } diff --git a/src/components/app/details/appDetails/appDetails.type.ts b/src/components/app/details/appDetails/appDetails.type.ts index 15dd50b690..b1eea20b10 100644 --- a/src/components/app/details/appDetails/appDetails.type.ts +++ b/src/components/app/details/appDetails/appDetails.type.ts @@ -22,15 +22,21 @@ import { AppEnvironment, DeploymentStatusDetailsBreakdownDataType, EnvAppsMetaDTO, + FiltersTypeEnum, OptionType, ResponseType, ScanRecommendationsDTO, ScanResultDTO, SelectPickerProps, ServerErrors, + TableCellComponentProps, } from '@devtron-labs/devtron-fe-common-lib' import { AggregatedNodes, UseGetDTAppDetailsReturnType } from '../../types' +import { + SecurityScanRecommendationRowTypes, + SecurityScanRecommendationTableAdditionalProps, +} from '../cIDetails/SecurityScanRecommendation/types' export enum AppMetricsTab { Aggregate = 'aggregate', @@ -192,10 +198,10 @@ export interface UseGetAppSecurityDetailsReturnType { reloadScanResult: () => void } export interface UseSecurityRecommendationReturnType { - scanResultLoading: boolean - scanResultResponse: ResponseType - scanResultError: ServerErrors - reloadScanResult: () => void + scanRecommendationsResultLoading: boolean + scanRecommendationsResultResponse: ResponseType + scanRecommendationsResultError: ServerErrors + reloadScanRecommendationsResult: () => void } export enum HibernationModalTypes { @@ -226,3 +232,24 @@ export type AppEnvSelectorProps = applications: EnvAppsMetaDTO['apps'] environments?: never } + +export type SecurityScanRecommendationColumn = { + label: string + field: string + size: { fixed: number } | null + CellComponent: ( + props: TableCellComponentProps< + SecurityScanRecommendationRowTypes, + FiltersTypeEnum.URL, + SecurityScanRecommendationTableAdditionalProps + >, + ) => JSX.Element | null +} & ( + | { + isSortable: true + comparator: (a: unknown, b: unknown) => number + } + | { + isSortable?: false + } +) diff --git a/src/components/app/details/cIDetails/CIDetails.tsx b/src/components/app/details/cIDetails/CIDetails.tsx index b49f468a16..362f069bd6 100644 --- a/src/components/app/details/cIDetails/CIDetails.tsx +++ b/src/components/app/details/cIDetails/CIDetails.tsx @@ -64,7 +64,7 @@ import { getModuleConfigured } from '../appDetails/appDetails.service' import { CIPipelineBuildType } from '../../../ciPipeline/types' import { renderCIListHeader, renderDeploymentHistoryTriggerMetaText } from '../cdDetails/utils' import { importComponentFromFELibrary } from '@Components/common' -import { useGetAppSecurityDetails } from '../appDetails/AppSecurity' +import { useGetAppSecurityDetails, useGetAppSecurityDetailsRecommendations } from '../appDetails/AppSecurity' import { SecurityScansRecommendations } from './SecurityScanRecommendation/SecurityScanRecommendations.components' const SecurityModalSidebar = importComponentFromFELibrary('SecurityModalSidebar', null, 'function') @@ -664,6 +664,16 @@ const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTabType) = artifactId, }) + const { + scanRecommendationsResultLoading, + scanRecommendationsResultResponse, + scanRecommendationsResultError, + reloadScanRecommendationsResult, + } = useGetAppSecurityDetailsRecommendations({ + appId: +computedAppId, + buildId: +buildId, + }) + const renderSecurityDetailsCards = () => { if (['starting', 'running'].includes(status.toLowerCase())) { return @@ -678,7 +688,7 @@ const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTabType) = ) } - if (scanResultLoading) { + if (scanResultLoading || scanRecommendationsResultLoading) { return (
@@ -697,7 +707,14 @@ const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTabType) = return (
- {isEnterprise && } + {isEnterprise && ( + + )} {renderSecurityDetailsCards()}
) diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecrityScanRecommendationBar.tsx b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecrityScanRecommendationBar.tsx index 536d1bafc3..76744242a6 100644 --- a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecrityScanRecommendationBar.tsx +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecrityScanRecommendationBar.tsx @@ -1,7 +1,14 @@ import dayjs from 'dayjs' -import { DATE_TIME_FORMATS, Icon, SegmentedBarChart, ZERO_TIME_STRING } from '@devtron-labs/devtron-fe-common-lib' +import { + DATE_TIME_FORMATS, + Icon, + ScannedByToolModal, + SegmentedBarChart, + ZERO_TIME_STRING, +} from '@devtron-labs/devtron-fe-common-lib' +import { HADOLINT_LINK } from './SecurityRecommendation.utils' import { SecurityScanRecommendationBarProps } from './types' export const SecurityScanRecommendationBar = ({ @@ -55,11 +62,14 @@ export const SecurityScanRecommendationBar = ({
{shouldShowLastScanTime ? ( -
- - - Scanned on {dayjs(lastScanTime).format(DATE_TIME_FORMATS.TWELVE_HOURS_FORMAT)}{' '} - +
+
+ + + Scanned on {dayjs(lastScanTime).format(DATE_TIME_FORMATS.TWELVE_HOURS_FORMAT)}{' '} + +
+
) : ( '' diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx index 2cd6335210..c7eec7ff1c 100644 --- a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx @@ -2,6 +2,7 @@ import { useEffect } from 'react' import { FiltersTypeEnum, stopPropagation, TableCellComponentProps } from '@devtron-labs/devtron-fe-common-lib' +import { SecurityScanRecommendationColumn } from '../../appDetails/appDetails.type' import { RecommendationResult, RecommendationSnippetLine, @@ -9,6 +10,30 @@ import { SecurityScanRecommendationTableAdditionalProps, } from './types' +const RECOMMENDATION_LEVEL_PRIORITY: Record = { + error: 0, + warning: 1, +} + +const levelComparator = (a: unknown, b: unknown): number => { + const aLevel = String(a || '').toLowerCase() + const bLevel = String(b || '').toLowerCase() + + const aPriority = RECOMMENDATION_LEVEL_PRIORITY[aLevel] ?? Number.MAX_SAFE_INTEGER + const bPriority = RECOMMENDATION_LEVEL_PRIORITY[bLevel] ?? Number.MAX_SAFE_INTEGER + + if (aPriority !== bPriority) { + return aPriority - bPriority + } + + return aLevel.localeCompare(bLevel) +} + +const codeComparator = (a: unknown, b: unknown): number => + String(a || '') + .toLowerCase() + .localeCompare(String(b || '').toLowerCase()) + export const getRecommendationRowId = (recommendation: RecommendationResult, index: number) => `recommendation-${recommendation.code || 'code'}-${index}` @@ -121,7 +146,7 @@ export const LevelCellComponent = ({ ) } -export const SECURITY_SCAN_RECOMMENDATIONS_TABLE_COLUMNS = [ +export const SECURITY_SCAN_RECOMMENDATIONS_TABLE_COLUMNS: SecurityScanRecommendationColumn[] = [ { label: 'Recommendations', field: 'title', @@ -133,6 +158,7 @@ export const SECURITY_SCAN_RECOMMENDATIONS_TABLE_COLUMNS = [ field: 'level', size: { fixed: 100 }, isSortable: true, + comparator: levelComparator, CellComponent: LevelCellComponent, }, { @@ -140,6 +166,9 @@ export const SECURITY_SCAN_RECOMMENDATIONS_TABLE_COLUMNS = [ field: 'code', size: { fixed: 150 }, isSortable: true, + comparator: codeComparator, CellComponent: CodeCellComponent, }, ] + +export const HADOLINT_LINK = 'https://hadolint.github.io/hadolint/' diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanRecommendations.components.tsx b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanRecommendations.components.tsx index 2c66f24766..b6f2f2b8fc 100644 --- a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanRecommendations.components.tsx +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanRecommendations.components.tsx @@ -7,26 +7,22 @@ import { ScanRecommendationsDTO, } from '@devtron-labs/devtron-fe-common-lib' -import { useGetAppSecurityDetailsRecommendations } from '../../appDetails/AppSecurity' import { SecurityScanRecommendationBar } from './SecrityScanRecommendationBar' +import { HADOLINT_LINK } from './SecurityRecommendation.utils' import { SecurityScanModal } from './SecurityScanModal' import { SecurityScansRecommendationsProps } from './types' -export const SecurityScansRecommendations = ({ appId, buildId }: SecurityScansRecommendationsProps) => { - const { scanResultLoading, scanResultResponse, scanResultError, reloadScanResult } = - useGetAppSecurityDetailsRecommendations({ - appId, - buildId, - }) +export const SecurityScansRecommendations = ({ + scanRecommendationLoading, + scanRecommendationResponse, + scanRecommendationError, + reloadScanRecommendation, +}: SecurityScansRecommendationsProps) => { const [showSecurityScanModal, setShowSecurityScanModal] = useState(false) - const recommendations: ScanRecommendationsDTO = scanResultResponse?.result + const recommendations: ScanRecommendationsDTO | undefined = scanRecommendationResponse?.result - if (!appId || !buildId) { - return null - } - - if (scanResultLoading) { + if (scanRecommendationLoading) { return (
@@ -36,8 +32,8 @@ export const SecurityScansRecommendations = ({ appId, buildId }: SecurityScansRe ) } - if (scanResultError) { - return + if (scanRecommendationError) { + return } if (!recommendations) { @@ -56,17 +52,14 @@ export const SecurityScansRecommendations = ({ appId, buildId }: SecurityScansRe {!hasRecommendations ? (
- No recommendations available for this scan. + No recommendations available for this scanRecommendation.
) : ( -
-
-

Dockerfile Linting

- +
+
+

Dockerfile Linting

+
+ scanRecommendationError: ServerErrors + reloadScanRecommendation: () => void } export type RecommendationResult = ScanRecommendationsDTO['results'][number] From 6c0618700df9ac1444d8ebbda0d1a455ea6fcf30 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Thu, 26 Mar 2026 12:56:52 +0530 Subject: [PATCH 07/38] chore: css fixes --- .../app/details/cIDetails/CIDetails.tsx | 2 +- ...SecurityScanRecommendations.components.tsx | 28 +------------- .../app/details/cIDetails/cIDetails.util.tsx | 38 +++++++++++++------ 3 files changed, 29 insertions(+), 39 deletions(-) diff --git a/src/components/app/details/cIDetails/CIDetails.tsx b/src/components/app/details/cIDetails/CIDetails.tsx index 362f069bd6..3ef4cac14f 100644 --- a/src/components/app/details/cIDetails/CIDetails.tsx +++ b/src/components/app/details/cIDetails/CIDetails.tsx @@ -706,7 +706,7 @@ const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTabType) = } return ( -
+
{isEnterprise && ( { +export const SecurityScansRecommendations = ({ scanRecommendationResponse }: SecurityScansRecommendationsProps) => { const [showSecurityScanModal, setShowSecurityScanModal] = useState(false) const recommendations: ScanRecommendationsDTO | undefined = scanRecommendationResponse?.result - if (scanRecommendationLoading) { - return ( -
-
- -
-
- ) - } - - if (scanRecommendationError) { - return - } - if (!recommendations) { return null } diff --git a/src/components/app/details/cIDetails/cIDetails.util.tsx b/src/components/app/details/cIDetails/cIDetails.util.tsx index 9a54a8ebdc..8d60d2b433 100644 --- a/src/components/app/details/cIDetails/cIDetails.util.tsx +++ b/src/components/app/details/cIDetails/cIDetails.util.tsx @@ -14,26 +14,40 @@ * limitations under the License. */ -import { GenericEmptyState, EMPTY_STATE_STATUS } from '@devtron-labs/devtron-fe-common-lib' -import { ReactComponent as ScannedDisabled } from '@Images/ic-empty-scanner-disabled.svg' -import { ReactComponent as MechanicalOperation } from '@Images/ic-mechanical-operation.svg' +import { EMPTY_STATE_STATUS, Icon, IconName } from '@devtron-labs/devtron-fe-common-lib' -export const ImageNotScannedView = () => { +export const SecurityScanEmptyState = ({ title, subtitle, icon= "ic-warning" }: { + title: string, + subtitle: string, + icon?: IconName +}) => { return ( - +
+ +
+
{title}
+
{subtitle}
+
+
+
+ ) +} + +export const ImageNotScannedView = (): JSX.Element => { + return ( + ) } -export const CIRunningView = (props) => { +export const CIRunningView = (props): JSX.Element => { return ( - ) -} +} \ No newline at end of file From dc5151b7a55992d15fea61bf5009106000d733ca Mon Sep 17 00:00:00 2001 From: shivani170 Date: Mon, 30 Mar 2026 16:10:52 +0530 Subject: [PATCH 08/38] chore: css fixes --- package.json | 2 +- .../app/details/cIDetails/CIDetails.tsx | 97 +++++++++++++------ .../SecrityScanRecommendationBar.tsx | 73 ++++++-------- .../SecurityRecommendation.utils.tsx | 14 ++- .../SecurityScanModal.tsx | 8 +- ...SecurityScanRecommendations.components.tsx | 33 ++++--- .../SecurityScanRecommendation/security.scss | 28 ++++-- .../SecurityScanRecommendation/types.ts | 3 +- .../app/details/cIDetails/cIDetails.util.tsx | 38 ++------ yarn.lock | 10 +- 10 files changed, 167 insertions(+), 139 deletions(-) diff --git a/package.json b/package.json index 24f731d1ae..cd04d36db0 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "1.23.3-beta-9", + "@devtron-labs/devtron-fe-common-lib": "1.23.3-beta-12", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rjsf/core": "^5.13.3", "@rjsf/utils": "^5.13.3", diff --git a/src/components/app/details/cIDetails/CIDetails.tsx b/src/components/app/details/cIDetails/CIDetails.tsx index 3ef4cac14f..fad90b7ef3 100644 --- a/src/components/app/details/cIDetails/CIDetails.tsx +++ b/src/components/app/details/cIDetails/CIDetails.tsx @@ -56,7 +56,7 @@ import { } from '../../service' import { URLS, Routes } from '../../../../config' import { BuildDetails, CIPipeline, HistoryLogsType, SecurityTabType } from './types' -import { ImageNotScannedView, CIRunningView } from './cIDetails.util' +import { CIRunningView } from './cIDetails.util' import './ciDetails.scss' import { getModuleInfo } from '../../../v2/devtronStackManager/DevtronStackManager.service' import { ModuleStatus } from '../../../v2/devtronStackManager/DevtronStackManager.type' @@ -66,6 +66,7 @@ import { renderCIListHeader, renderDeploymentHistoryTriggerMetaText } from '../c import { importComponentFromFELibrary } from '@Components/common' import { useGetAppSecurityDetails, useGetAppSecurityDetailsRecommendations } from '../appDetails/AppSecurity' import { SecurityScansRecommendations } from './SecurityScanRecommendation/SecurityScanRecommendations.components' +import { getSecurityScanRecommendationTitle } from './SecurityScanRecommendation/SecurityRecommendation.utils' const SecurityModalSidebar = importComponentFromFELibrary('SecurityModalSidebar', null, 'function') const terminalStatus = new Set(['succeeded', 'failed', 'error', 'cancelled', 'nottriggered', 'notbuilt']) @@ -488,7 +489,7 @@ export const Details = ({ ? [ { id: 'security-tab', - label: 'Security', + label: 'Reports', tabType: 'navLink' as const, props: { to: 'security', @@ -653,6 +654,38 @@ const HistoryLogs = ({ ) } +const renderSecurityScanRecommendation = (appId: number, buildId: number) => { + const { + scanRecommendationsResultLoading, + scanRecommendationsResultResponse, + scanRecommendationsResultError, + reloadScanRecommendationsResult, + } = useGetAppSecurityDetailsRecommendations({ + appId, + buildId, + }) + + return ( +
+ {getSecurityScanRecommendationTitle()} + {scanRecommendationsResultError && ( +
+ +
+ )} + +
+ ) +} + const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTabType) => { const { appId, buildId } = useParams<{ appId: string; buildId: string }>() const { isEnterprise } = useMainContext() @@ -664,27 +697,39 @@ const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTabType) = artifactId, }) - const { - scanRecommendationsResultLoading, - scanRecommendationsResultResponse, - scanRecommendationsResultError, - reloadScanRecommendationsResult, - } = useGetAppSecurityDetailsRecommendations({ + const { scanRecommendationsResultLoading } = useGetAppSecurityDetailsRecommendations({ appId: +computedAppId, buildId: +buildId, }) + const renderHeader = () => ( +
+ Security Scan +
+ ) const renderSecurityDetailsCards = () => { - if (['starting', 'running'].includes(status.toLowerCase())) { - return + const normalizedStatus = status?.toLowerCase() + + if (['starting', 'running'].includes(normalizedStatus)) { + return ( + <> + {renderHeader()} + + + ) } if (!artifactId) { return ( - + <> + {renderHeader()} +
+ +
+ ) } @@ -695,26 +740,24 @@ const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTabType) =
) } + if (scanResultError) { - return - } - if (!scanResultResponse?.result.scanned) { - return + return ( + <> + {renderHeader()} +
+ +
+ + ) } return } return ( -
- {isEnterprise && ( - - )} +
+ {isEnterprise && renderSecurityScanRecommendation(+computedAppId, +buildId)} {renderSecurityDetailsCards()}
) diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecrityScanRecommendationBar.tsx b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecrityScanRecommendationBar.tsx index 76744242a6..5136cec782 100644 --- a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecrityScanRecommendationBar.tsx +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecrityScanRecommendationBar.tsx @@ -13,69 +13,54 @@ import { SecurityScanRecommendationBarProps } from './types' export const SecurityScanRecommendationBar = ({ summary, - hasRecommendations, handleSecurityScanModal, isModalView = false, lastScanTime, }: SecurityScanRecommendationBarProps) => { const { error, warning } = summary const totalCount = error + warning - const hasThreats = error || warning const shouldShowLastScanTime = isModalView && !!lastScanTime && lastScanTime !== ZERO_TIME_STRING && lastScanTime !== 0 return (
- {!hasThreats || !hasRecommendations ? ( - <> -
- Dockerfile Best Practices - Looks good! -
- -
-
- No recommendations suggested -
- - ) : ( -
-
+
+
+
{!isModalView && Dockerfile Best Practices} {totalCount} Recommendations
-
- -
+ +
+
+
- {shouldShowLastScanTime ? ( -
-
- - - Scanned on {dayjs(lastScanTime).format(DATE_TIME_FORMATS.TWELVE_HOURS_FORMAT)}{' '} - -
- -
- ) : ( - '' - )}
- )} + {shouldShowLastScanTime ? ( +
+
+ + + Scanned on {dayjs(lastScanTime).format(DATE_TIME_FORMATS.TWELVE_HOURS_FORMAT)}{' '} + +
+ +
+ ) : null} +
) } diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx index c7eec7ff1c..168da90f4f 100644 --- a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx @@ -1,6 +1,11 @@ import { useEffect } from 'react' -import { FiltersTypeEnum, stopPropagation, TableCellComponentProps } from '@devtron-labs/devtron-fe-common-lib' +import { + FiltersTypeEnum, + ScannedByToolModal, + stopPropagation, + TableCellComponentProps, +} from '@devtron-labs/devtron-fe-common-lib' import { SecurityScanRecommendationColumn } from '../../appDetails/appDetails.type' import { @@ -172,3 +177,10 @@ export const SECURITY_SCAN_RECOMMENDATIONS_TABLE_COLUMNS: SecurityScanRecommenda ] export const HADOLINT_LINK = 'https://hadolint.github.io/hadolint/' + +export const getSecurityScanRecommendationTitle = () => ( +
+

Dockerfile Linting

+ +
+) diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanModal.tsx b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanModal.tsx index ba028b1715..f1bc7fb240 100644 --- a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanModal.tsx +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanModal.tsx @@ -28,7 +28,6 @@ const CLOSE_BUTTON_ID = 'security-scan-recommendations-close' export const SecurityScanModal = ({ summary, - hasRecommendations, recommendations, handleSecurityScanModal, lastScanTime, @@ -126,12 +125,7 @@ export const SecurityScanModal = ({ />
- + { setShowSecurityScanModal((currentState) => !currentState) } + const { error, warning } = severitySummary + + const hasThreats = error || warning return (
- {!hasRecommendations ? ( -
- - No recommendations available for this scanRecommendation. - + {!hasThreats || !hasRecommendations ? ( +
+
+
+ Dockerfile Best Practices + Looks good! +
+ +
+ +
+
+ No recommendations suggested +
) : (
-
-

Dockerfile Linting

- -
{showSecurityScanModal && ( .generic-table__cell { + .generic-table__row--is-expanded>.generic-table__cell { background-color: var(--bg-secondary); } .generic-table__row--is-expanded:hover, - .generic-table__row--is-expanded:hover > .generic-table__cell, + .generic-table__row--is-expanded:hover>.generic-table__cell, .generic-table__row--is-expanded.generic-table__row--active, - .generic-table__row--is-expanded.generic-table__row--active > .generic-table__cell { + .generic-table__row--is-expanded.generic-table__row--active>.generic-table__cell { background-color: var(--bg-secondary); } @@ -105,17 +115,17 @@ background-color: transparent; } - .generic-table__row--expanded-row > .generic-table__cell, - .generic-table__row--expanded-row > .expanded-tree-line { + .generic-table__row--expanded-row>.generic-table__cell, + .generic-table__row--expanded-row>.expanded-tree-line { background-color: transparent; } .generic-table__row--expanded-row:hover, - .generic-table__row--expanded-row:hover > .generic-table__cell, - .generic-table__row--expanded-row:hover > .expanded-tree-line, + .generic-table__row--expanded-row:hover>.generic-table__cell, + .generic-table__row--expanded-row:hover>.expanded-tree-line, .generic-table__row--expanded-row.generic-table__row--active, - .generic-table__row--expanded-row.generic-table__row--active > .generic-table__cell, - .generic-table__row--expanded-row.generic-table__row--active > .expanded-tree-line { + .generic-table__row--expanded-row.generic-table__row--active>.generic-table__cell, + .generic-table__row--expanded-row.generic-table__row--active>.expanded-tree-line { background-color: transparent; } } \ No newline at end of file diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/types.ts b/src/components/app/details/cIDetails/SecurityScanRecommendation/types.ts index 42adc17fdc..d01976521e 100644 --- a/src/components/app/details/cIDetails/SecurityScanRecommendation/types.ts +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/types.ts @@ -19,7 +19,6 @@ export type RecommendationSnippetLine = { export type SecurityScanRecommendationModalProps = { summary: ScanRecommendationsDTO['severity_summary'] - hasRecommendations: boolean recommendations: ScanRecommendationsDTO['results'] handleSecurityScanModal?: () => void lastScanTime?: ScanRecommendationsDTO['createdOn'] | string @@ -28,7 +27,7 @@ export type SecurityScanRecommendationModalProps = { export type SecurityScanRecommendationBarProps = Pick< SecurityScanRecommendationModalProps, - 'hasRecommendations' | 'summary' | 'handleSecurityScanModal' | 'isModalView' | 'lastScanTime' + 'summary' | 'handleSecurityScanModal' | 'isModalView' | 'lastScanTime' > & {} export type SecurityScanRecommendationRowTypes = { diff --git a/src/components/app/details/cIDetails/cIDetails.util.tsx b/src/components/app/details/cIDetails/cIDetails.util.tsx index 8d60d2b433..d9bd73a412 100644 --- a/src/components/app/details/cIDetails/cIDetails.util.tsx +++ b/src/components/app/details/cIDetails/cIDetails.util.tsx @@ -14,40 +14,18 @@ * limitations under the License. */ -import { EMPTY_STATE_STATUS, Icon, IconName } from '@devtron-labs/devtron-fe-common-lib' +import { EMPTY_STATE_STATUS, GenericEmptyState } from '@devtron-labs/devtron-fe-common-lib' +import { ReactComponent as MechanicalOperation } from '@Images/ic-mechanical-operation.svg' -export const SecurityScanEmptyState = ({ title, subtitle, icon= "ic-warning" }: { - title: string, - subtitle: string, - icon?: IconName -}) => { - return ( -
-
- -
-
{title}
-
{subtitle}
-
-
-
- ) -} - -export const ImageNotScannedView = (): JSX.Element => { - return ( - - ) -} -export const CIRunningView = (props): JSX.Element => { +export const CIRunningView = (props) => { return ( - + +
) } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index c1519c5f22..27dab3aeba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1729,9 +1729,9 @@ __metadata: languageName: node linkType: hard -"@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-9": - version: 1.23.3-beta-9 - resolution: "@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-9" +"@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-12": + version: 1.23.3-beta-12 + resolution: "@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-12" dependencies: "@codemirror/autocomplete": "npm:6.18.6" "@codemirror/lang-json": "npm:6.0.1" @@ -1785,7 +1785,7 @@ __metadata: react-select: 5.8.0 rxjs: ^7.8.1 yaml: ^2.4.1 - checksum: 10c0/6d3c86a4491bed9273f5bc8e216327ef114f2f537237ad851cc04124506c4fdc24c15a4d469f97d627e69b309599801f19fd58ac5491a8fac4123afb3cf07198 + checksum: 10c0/43f0adef905fb97f5cc0470ffc62c02a0c8d7cfb02c882a29388406f0d8fd3f8f4cbeac0f31bbbb6497f6cd39f916179a55f10bedaa60f1e61bcab18d96d2393 languageName: node linkType: hard @@ -5599,7 +5599,7 @@ __metadata: version: 0.0.0-use.local resolution: "dashboard@workspace:." dependencies: - "@devtron-labs/devtron-fe-common-lib": "npm:1.23.3-beta-9" + "@devtron-labs/devtron-fe-common-lib": "npm:1.23.3-beta-12" "@esbuild-plugins/node-globals-polyfill": "npm:0.2.3" "@playwright/test": "npm:^1.32.1" "@rjsf/core": "npm:^5.13.3" From 4c4b8d4543f25929e69247aa27542f956dfe6a4d Mon Sep 17 00:00:00 2001 From: shivani170 Date: Mon, 30 Mar 2026 17:35:26 +0530 Subject: [PATCH 09/38] fix: dockerfileScanEnabled FORCE_DOCKERFILE_SCAN cm integration --- .env | 1 + config.md | 4 +++- package.json | 2 +- src/components/CIPipelineN/Build.tsx | 2 +- src/components/CIPipelineN/CIPipeline.tsx | 8 ++++++-- src/components/ciPipeline/ciPipeline.service.ts | 4 +++- src/index.tsx | 1 + yarn.lock | 10 +++++----- 8 files changed, 21 insertions(+), 11 deletions(-) diff --git a/.env b/.env index 2351c2b8a6..074eb18031 100644 --- a/.env +++ b/.env @@ -13,6 +13,7 @@ POSTHOG_ENABLED= POSTHOG_TOKEN= RECOMMEND_SECURITY_SCANNING=false FORCE_SECURITY_SCANNING=false +FORCE_DOCKERFILE_SCAN=false ENABLE_CI_JOB=false HIDE_DISCORD=false DEVTRON_APP_DETAILS_POLLING_INTERVAL=30000 diff --git a/config.md b/config.md index 64997ab9aa..03b00801af 100644 --- a/config.md +++ b/config.md @@ -18,7 +18,9 @@ | ENABLE_CHART_SEARCH_IN_HELM_DEPLOY | "true" | Enable chart search in Helm deploy | | ENABLE_EXTERNAL_ARGO_CD | "true" | Enable External Argo CD | | ENABLE_SCOPED_VARIABLES | "false" | For enabling scoped variable from UI, also need to enable it in backend. | -| FORCE_SECURITY_SCANNING | "false" | Force security scanning | +| FORCE_SECURITY_SCANNING | "false" | Force security +scanning | +| FORCE_DOCKERFILE_SCAN | "false" | Force dockerfile scan | | GA_ENABLED | "true" | Enable Google Analytics (GA) | | GA_TRACKING_ID | G-XXXXXXXX | Google Analytics tracking ID | | GLOBAL_API_TIMEOUT | 60000 | Default timeout for all API requests in DASHBOARD | diff --git a/package.json b/package.json index cd04d36db0..81d7679123 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "1.23.3-beta-12", + "@devtron-labs/devtron-fe-common-lib": "1.23.3-beta-13", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rjsf/core": "^5.13.3", "@rjsf/utils": "^5.13.3", diff --git a/src/components/CIPipelineN/Build.tsx b/src/components/CIPipelineN/Build.tsx index c79d79ee4c..e3b8c44079 100644 --- a/src/components/CIPipelineN/Build.tsx +++ b/src/components/CIPipelineN/Build.tsx @@ -252,7 +252,7 @@ export const Build = ({

{ const scanEnabled = window._env_ && (window._env_.RECOMMEND_SECURITY_SCANNING || window._env_.FORCE_SECURITY_SCANNING) + const dockerfileScanEnabled = + window._env_ && (window._env_.RECOMMEND_SECURITY_SCANNING || window._env_.FORCE_DOCKERFILE_SCAN) return { result: { form: { @@ -102,7 +104,7 @@ export function getInitData( preBuildStage: emptyStepsData(), postBuildStage: emptyStepsData(), scanEnabled, - dockerfileScanEnabled: true, + dockerfileScanEnabled: dockerfileScanEnabled, ciPipelineEditable: true, workflowCacheConfig: pipelineMetaConfig.result.workflowCacheConfig ?? null, }, diff --git a/src/index.tsx b/src/index.tsx index 8eca26aca9..5a66482c3d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -117,6 +117,7 @@ if (!window || !window._env_) { POSTHOG_TOKEN: '', RECOMMEND_SECURITY_SCANNING: false, FORCE_SECURITY_SCANNING: false, + FORCE_DOCKERFILE_SCAN: false, ENABLE_CI_JOB: true, HIDE_DISCORD: true, DEVTRON_APP_DETAILS_POLLING_INTERVAL: 30000, diff --git a/yarn.lock b/yarn.lock index 27dab3aeba..525c1defee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1729,9 +1729,9 @@ __metadata: languageName: node linkType: hard -"@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-12": - version: 1.23.3-beta-12 - resolution: "@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-12" +"@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-13": + version: 1.23.3-beta-13 + resolution: "@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-13" dependencies: "@codemirror/autocomplete": "npm:6.18.6" "@codemirror/lang-json": "npm:6.0.1" @@ -1785,7 +1785,7 @@ __metadata: react-select: 5.8.0 rxjs: ^7.8.1 yaml: ^2.4.1 - checksum: 10c0/43f0adef905fb97f5cc0470ffc62c02a0c8d7cfb02c882a29388406f0d8fd3f8f4cbeac0f31bbbb6497f6cd39f916179a55f10bedaa60f1e61bcab18d96d2393 + checksum: 10c0/96059b7382dfd7567073ccb9b656f4101c1a9486bc4587ce1edf01cf7ed993fd603ae7d54ff0cdca2ed8ef3a9eca6c492a206cb2e06e9153fa899142b98050ef languageName: node linkType: hard @@ -5599,7 +5599,7 @@ __metadata: version: 0.0.0-use.local resolution: "dashboard@workspace:." dependencies: - "@devtron-labs/devtron-fe-common-lib": "npm:1.23.3-beta-12" + "@devtron-labs/devtron-fe-common-lib": "npm:1.23.3-beta-13" "@esbuild-plugins/node-globals-polyfill": "npm:0.2.3" "@playwright/test": "npm:^1.32.1" "@rjsf/core": "npm:^5.13.3" From a0ce557a299bc9ec4deb406953566a975446bc61 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 31 Mar 2026 11:01:00 +0530 Subject: [PATCH 10/38] chore: version bump --- package.json | 2 +- .../app/details/cIDetails/CIDetails.tsx | 36 ++++++++++--------- yarn.lock | 10 +++--- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index 81d7679123..84e98eff42 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "1.23.3-beta-13", + "@devtron-labs/devtron-fe-common-lib": "1.23.3-beta-14", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rjsf/core": "^5.13.3", "@rjsf/utils": "^5.13.3", diff --git a/src/components/app/details/cIDetails/CIDetails.tsx b/src/components/app/details/cIDetails/CIDetails.tsx index fad90b7ef3..a226d1397b 100644 --- a/src/components/app/details/cIDetails/CIDetails.tsx +++ b/src/components/app/details/cIDetails/CIDetails.tsx @@ -67,6 +67,7 @@ import { importComponentFromFELibrary } from '@Components/common' import { useGetAppSecurityDetails, useGetAppSecurityDetailsRecommendations } from '../appDetails/AppSecurity' import { SecurityScansRecommendations } from './SecurityScanRecommendation/SecurityScanRecommendations.components' import { getSecurityScanRecommendationTitle } from './SecurityScanRecommendation/SecurityRecommendation.utils' +import { ReactComponent as MechanicalOperation } from '@Images/ic-mechanical-operation.svg' const SecurityModalSidebar = importComponentFromFELibrary('SecurityModalSidebar', null, 'function') const terminalStatus = new Set(['succeeded', 'failed', 'error', 'cancelled', 'nottriggered', 'notbuilt']) @@ -670,10 +671,18 @@ const renderSecurityScanRecommendation = (appId: number, buildId: number) => { {getSecurityScanRecommendationTitle()} {scanRecommendationsResultError && (
- + {scanRecommendationsResultError.code === 404 ? ( + + ) : ( + + )}
)} (
Security Scan @@ -712,16 +716,16 @@ const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTabType) = if (['starting', 'running'].includes(normalizedStatus)) { return ( - <> +
{renderHeader()} - +
) } if (!artifactId) { return ( - <> +
{renderHeader()}
- +
) } - if (scanResultLoading || scanRecommendationsResultLoading) { + if (scanResultLoading) { return (
@@ -743,12 +747,12 @@ const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTabType) = if (scanResultError) { return ( - <> +
{renderHeader()}
- +
) } diff --git a/yarn.lock b/yarn.lock index 525c1defee..97343004e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1729,9 +1729,9 @@ __metadata: languageName: node linkType: hard -"@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-13": - version: 1.23.3-beta-13 - resolution: "@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-13" +"@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-14": + version: 1.23.3-beta-14 + resolution: "@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-14" dependencies: "@codemirror/autocomplete": "npm:6.18.6" "@codemirror/lang-json": "npm:6.0.1" @@ -1785,7 +1785,7 @@ __metadata: react-select: 5.8.0 rxjs: ^7.8.1 yaml: ^2.4.1 - checksum: 10c0/96059b7382dfd7567073ccb9b656f4101c1a9486bc4587ce1edf01cf7ed993fd603ae7d54ff0cdca2ed8ef3a9eca6c492a206cb2e06e9153fa899142b98050ef + checksum: 10c0/e1875424928736741d26590a311963d94fff11ca4e0ac7113876aea043352206dc72235a122e7f1a7ac1e0eb96a96476a05a2f9821002c29388359fe07d10358 languageName: node linkType: hard @@ -5599,7 +5599,7 @@ __metadata: version: 0.0.0-use.local resolution: "dashboard@workspace:." dependencies: - "@devtron-labs/devtron-fe-common-lib": "npm:1.23.3-beta-13" + "@devtron-labs/devtron-fe-common-lib": "npm:1.23.3-beta-14" "@esbuild-plugins/node-globals-polyfill": "npm:0.2.3" "@playwright/test": "npm:^1.32.1" "@rjsf/core": "npm:^5.13.3" From 33075419fd9bb1326e6b0b4530b74ed9606bd993 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 31 Mar 2026 12:11:42 +0530 Subject: [PATCH 11/38] chore: css fixes --- src/components/app/details/cIDetails/CIDetails.tsx | 1 - src/components/app/details/cIDetails/cIDetails.util.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/app/details/cIDetails/CIDetails.tsx b/src/components/app/details/cIDetails/CIDetails.tsx index a226d1397b..7c201c075b 100644 --- a/src/components/app/details/cIDetails/CIDetails.tsx +++ b/src/components/app/details/cIDetails/CIDetails.tsx @@ -674,7 +674,6 @@ const renderSecurityScanRecommendation = (appId: number, buildId: number) => { {scanRecommendationsResultError.code === 404 ? ( ) : ( diff --git a/src/components/app/details/cIDetails/cIDetails.util.tsx b/src/components/app/details/cIDetails/cIDetails.util.tsx index d9bd73a412..39dd21e0b6 100644 --- a/src/components/app/details/cIDetails/cIDetails.util.tsx +++ b/src/components/app/details/cIDetails/cIDetails.util.tsx @@ -20,7 +20,7 @@ import { ReactComponent as MechanicalOperation } from '@Images/ic-mechanical-ope export const CIRunningView = (props) => { return ( -
+
Date: Tue, 31 Mar 2026 14:08:21 +0530 Subject: [PATCH 12/38] chore: info integration fixes --- .../app/details/cIDetails/CIDetails.tsx | 21 ++++++++++----- .../SecrityScanRecommendationBar.tsx | 3 ++- .../SecurityRecommendation.utils.tsx | 2 ++ ...SecurityScanRecommendations.components.tsx | 26 ++++++++++++++++--- 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/components/app/details/cIDetails/CIDetails.tsx b/src/components/app/details/cIDetails/CIDetails.tsx index 7c201c075b..c1cf35d276 100644 --- a/src/components/app/details/cIDetails/CIDetails.tsx +++ b/src/components/app/details/cIDetails/CIDetails.tsx @@ -666,8 +666,19 @@ const renderSecurityScanRecommendation = (appId: number, buildId: number) => { buildId, }) + if(scanRecommendationsResultLoading) { + return ( +
+ {getSecurityScanRecommendationTitle()} +
+ +
+
+ ) + } + return ( -
+
{getSecurityScanRecommendationTitle()} {scanRecommendationsResultError && (
@@ -687,8 +698,6 @@ const renderSecurityScanRecommendation = (appId: number, buildId: number) => {
) @@ -715,7 +724,7 @@ const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTabType) = if (['starting', 'running'].includes(normalizedStatus)) { return ( -
+
{renderHeader()}
@@ -724,7 +733,7 @@ const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTabType) = if (!artifactId) { return ( -
+
{renderHeader()}
+
{renderHeader()}
diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecrityScanRecommendationBar.tsx b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecrityScanRecommendationBar.tsx index 5136cec782..6a48bd0ae5 100644 --- a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecrityScanRecommendationBar.tsx +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecrityScanRecommendationBar.tsx @@ -17,7 +17,7 @@ export const SecurityScanRecommendationBar = ({ isModalView = false, lastScanTime, }: SecurityScanRecommendationBarProps) => { - const { error, warning } = summary + const { error, warning, info } = summary const totalCount = error + warning const shouldShowLastScanTime = isModalView && !!lastScanTime && lastScanTime !== ZERO_TIME_STRING && lastScanTime !== 0 @@ -41,6 +41,7 @@ export const SecurityScanRecommendationBar = ({ entities={[ { color: 'var(--Y500)', label: 'Warnings', value: warning || 0 }, { color: 'var(--R500)', label: 'Errors', value: error || 0 }, + { color: 'var(--N500)', label: 'Info', value: info || 0 }, ]} labelClassName="fs-13 fw-4 lh-20" countClassName="fs-13 fw-6 lh-20 cn-7" diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx index 168da90f4f..c8caba62be 100644 --- a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx @@ -15,6 +15,8 @@ import { SecurityScanRecommendationTableAdditionalProps, } from './types' +import './security.scss' + const RECOMMENDATION_LEVEL_PRIORITY: Record = { error: 0, warning: 1, diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanRecommendations.components.tsx b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanRecommendations.components.tsx index 8997128709..cf057f25aa 100644 --- a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanRecommendations.components.tsx +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanRecommendations.components.tsx @@ -1,12 +1,15 @@ import { useState } from 'react' -import { Icon, ScanRecommendationsDTO } from '@devtron-labs/devtron-fe-common-lib' +import { Icon, Progressing, ReportTabEmptyState, ScanRecommendationsDTO } from '@devtron-labs/devtron-fe-common-lib' import { SecurityScanRecommendationBar } from './SecrityScanRecommendationBar' import { SecurityScanModal } from './SecurityScanModal' import { SecurityScansRecommendationsProps } from './types' -export const SecurityScansRecommendations = ({ scanRecommendationResponse }: SecurityScansRecommendationsProps) => { +export const SecurityScansRecommendations = ({ + scanRecommendationLoading, + scanRecommendationResponse, +}: SecurityScansRecommendationsProps) => { const [showSecurityScanModal, setShowSecurityScanModal] = useState(false) const recommendations: ScanRecommendationsDTO | undefined = scanRecommendationResponse?.result @@ -15,7 +18,7 @@ export const SecurityScansRecommendations = ({ scanRecommendationResponse }: Sec return null } - const { results, severity_summary: severitySummary } = recommendations + const { results, severity_summary: severitySummary, dockerfileScanEnabled } = recommendations const hasRecommendations = !!results?.length const handleSecurityScanModal = () => { @@ -25,6 +28,23 @@ export const SecurityScansRecommendations = ({ scanRecommendationResponse }: Sec const hasThreats = error || warning + if (!dockerfileScanEnabled && !hasRecommendations) { + return ( + + ) + } + + if (scanRecommendationLoading) { + return ( +
+ +
+ ) + } + return (
{!hasThreats || !hasRecommendations ? ( From cc895cf02b7e75e99b3bc8cd0bfe098cbb7e777d Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 31 Mar 2026 16:04:25 +0530 Subject: [PATCH 13/38] chore: forceDockerfileScan added --- .../app/details/cIDetails/CIDetails.tsx | 132 +--------------- .../SecurityScanRecommendation/types.ts | 4 +- .../app/details/cIDetails/SecurityTab.tsx | 148 ++++++++++++++++++ .../common/navigation/NavigationRoutes.tsx | 3 + src/components/common/navigation/constants.ts | 1 + 5 files changed, 155 insertions(+), 133 deletions(-) create mode 100644 src/components/app/details/cIDetails/SecurityTab.tsx diff --git a/src/components/app/details/cIDetails/CIDetails.tsx b/src/components/app/details/cIDetails/CIDetails.tsx index c1cf35d276..721c74fdce 100644 --- a/src/components/app/details/cIDetails/CIDetails.tsx +++ b/src/components/app/details/cIDetails/CIDetails.tsx @@ -41,10 +41,7 @@ import { EMPTY_STATE_STATUS, TabGroup, TRIGGER_STATUS_PROGRESSING, - ErrorScreenManager, - SecurityDetailsCards, sanitizeTargetPlatforms, - useMainContext, } from '@devtron-labs/devtron-fe-common-lib' import { Switch, Route, Redirect, useRouteMatch, useParams, useHistory, generatePath } from 'react-router-dom' import { @@ -55,21 +52,15 @@ import { getArtifactForJobCi, } from '../../service' import { URLS, Routes } from '../../../../config' -import { BuildDetails, CIPipeline, HistoryLogsType, SecurityTabType } from './types' -import { CIRunningView } from './cIDetails.util' +import { BuildDetails, CIPipeline, HistoryLogsType } from './types' import './ciDetails.scss' import { getModuleInfo } from '../../../v2/devtronStackManager/DevtronStackManager.service' import { ModuleStatus } from '../../../v2/devtronStackManager/DevtronStackManager.type' import { getModuleConfigured } from '../appDetails/appDetails.service' import { CIPipelineBuildType } from '../../../ciPipeline/types' import { renderCIListHeader, renderDeploymentHistoryTriggerMetaText } from '../cdDetails/utils' -import { importComponentFromFELibrary } from '@Components/common' -import { useGetAppSecurityDetails, useGetAppSecurityDetailsRecommendations } from '../appDetails/AppSecurity' -import { SecurityScansRecommendations } from './SecurityScanRecommendation/SecurityScanRecommendations.components' -import { getSecurityScanRecommendationTitle } from './SecurityScanRecommendation/SecurityRecommendation.utils' -import { ReactComponent as MechanicalOperation } from '@Images/ic-mechanical-operation.svg' +import { SecurityTab } from './SecurityTab' -const SecurityModalSidebar = importComponentFromFELibrary('SecurityModalSidebar', null, 'function') const terminalStatus = new Set(['succeeded', 'failed', 'error', 'cancelled', 'nottriggered', 'notbuilt']) const statusSet = new Set(['starting', 'running', 'pending']) @@ -655,122 +646,3 @@ const HistoryLogs = ({ ) } -const renderSecurityScanRecommendation = (appId: number, buildId: number) => { - const { - scanRecommendationsResultLoading, - scanRecommendationsResultResponse, - scanRecommendationsResultError, - reloadScanRecommendationsResult, - } = useGetAppSecurityDetailsRecommendations({ - appId, - buildId, - }) - - if(scanRecommendationsResultLoading) { - return ( -
- {getSecurityScanRecommendationTitle()} -
- -
-
- ) - } - - return ( -
- {getSecurityScanRecommendationTitle()} - {scanRecommendationsResultError && ( -
- {scanRecommendationsResultError.code === 404 ? ( - - ) : ( - - )} -
- )} - -
- ) -} - -const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTabType) => { - const { appId, buildId } = useParams<{ appId: string; buildId: string }>() - const { isEnterprise } = useMainContext() - - const computedAppId = appId ?? appIdFromParent - - const { scanResultLoading, scanResultResponse, scanResultError, reloadScanResult } = useGetAppSecurityDetails({ - appId: +computedAppId, - artifactId, - }) - - const renderHeader = () => ( -
- Security Scan -
- ) - const renderSecurityDetailsCards = () => { - const normalizedStatus = status?.toLowerCase() - - if (['starting', 'running'].includes(normalizedStatus)) { - return ( -
- {renderHeader()} - -
- ) - } - - if (!artifactId) { - return ( -
- {renderHeader()} -
- -
-
- ) - } - - if (scanResultLoading) { - return ( -
- -
- ) - } - - if (scanResultError) { - return ( -
- {renderHeader()} -
- -
-
- ) - } - - return - } - - return ( -
- {isEnterprise && renderSecurityScanRecommendation(+computedAppId, +buildId)} - {renderSecurityDetailsCards()} -
- ) -} diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/types.ts b/src/components/app/details/cIDetails/SecurityScanRecommendation/types.ts index d01976521e..ba3db1da50 100644 --- a/src/components/app/details/cIDetails/SecurityScanRecommendation/types.ts +++ b/src/components/app/details/cIDetails/SecurityScanRecommendation/types.ts @@ -1,12 +1,10 @@ import type { MouseEvent } from 'react' -import { ResponseType, ScanRecommendationsDTO, ServerErrors } from '@devtron-labs/devtron-fe-common-lib' +import { ResponseType, ScanRecommendationsDTO } from '@devtron-labs/devtron-fe-common-lib' export interface SecurityScansRecommendationsProps { scanRecommendationLoading: boolean scanRecommendationResponse: ResponseType - scanRecommendationError: ServerErrors - reloadScanRecommendation: () => void } export type RecommendationResult = ScanRecommendationsDTO['results'][number] diff --git a/src/components/app/details/cIDetails/SecurityTab.tsx b/src/components/app/details/cIDetails/SecurityTab.tsx new file mode 100644 index 0000000000..408742da5c --- /dev/null +++ b/src/components/app/details/cIDetails/SecurityTab.tsx @@ -0,0 +1,148 @@ +import { useParams } from 'react-router-dom' + +import { + ErrorScreenManager, + GenericEmptyState, + Progressing, + ReportTabEmptyState, + SecurityDetailsCards, + useMainContext, +} from '@devtron-labs/devtron-fe-common-lib' + +import { ReactComponent as MechanicalOperation } from '@Images/ic-mechanical-operation.svg' +import { importComponentFromFELibrary } from '@Components/common' +import { EMPTY_STATE_STATUS } from '@Config/constantMessaging' + +import { useGetAppSecurityDetails, useGetAppSecurityDetailsRecommendations } from '../appDetails/AppSecurity' +import { getSecurityScanRecommendationTitle } from './SecurityScanRecommendation/SecurityRecommendation.utils' +import { SecurityScansRecommendations } from './SecurityScanRecommendation/SecurityScanRecommendations.components' +import { CIRunningView } from './cIDetails.util' +import { SecurityTabType } from './types' + +const SecurityModalSidebar = importComponentFromFELibrary('SecurityModalSidebar', null, 'function') + +export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTabType) => { + const { appId, buildId } = useParams<{ appId: string; buildId: string }>() + const { isEnterprise, forceDockerfileScan } = useMainContext() + + const computedAppId = appId ?? appIdFromParent + + const { scanResultLoading, scanResultResponse, scanResultError, reloadScanResult } = useGetAppSecurityDetails({ + appId: +computedAppId, + artifactId, + }) + + const { + scanRecommendationsResultLoading, + scanRecommendationsResultResponse, + scanRecommendationsResultError, + reloadScanRecommendationsResult, + } = useGetAppSecurityDetailsRecommendations({ + appId: +computedAppId, + buildId: +buildId, + }) + + const renderSecurityScanRecommendation = () => { + if (!forceDockerfileScan && !scanRecommendationsResultResponse?.result?.dockerfileScanEnabled) { + return ( + + ) + } + + if (scanRecommendationsResultLoading) { + return ( +
+ {getSecurityScanRecommendationTitle()} +
+ +
+
+ ) + } + + return ( +
+ {getSecurityScanRecommendationTitle()} + {scanRecommendationsResultError && ( +
+ {scanRecommendationsResultError.code === 404 ? ( + + ) : ( + + )} +
+ )} + +
+ ) + } + + const renderHeader = () => ( +
+ Security Scan +
+ ) + const renderSecurityDetailsCards = () => { + const normalizedStatus = status?.toLowerCase() + + if (['starting', 'running'].includes(normalizedStatus)) { + return ( +
+ {renderHeader()} + +
+ ) + } + + if (!artifactId) { + return ( +
+ {renderHeader()} +
+ +
+
+ ) + } + + if (scanResultLoading) { + return ( +
+ +
+ ) + } + + if (scanResultError) { + return ( +
+ {renderHeader()} +
+ +
+
+ ) + } + + return + } + + return ( +
+ {isEnterprise && renderSecurityScanRecommendation()} + {renderSecurityDetailsCards()} +
+ ) +} diff --git a/src/components/common/navigation/NavigationRoutes.tsx b/src/components/common/navigation/NavigationRoutes.tsx index bed5e4d1c7..0dcdec5117 100644 --- a/src/components/common/navigation/NavigationRoutes.tsx +++ b/src/components/common/navigation/NavigationRoutes.tsx @@ -420,6 +420,7 @@ const NavigationRoutes = ({ reloadVersionConfig }: Readonly diff --git a/src/components/common/navigation/constants.ts b/src/components/common/navigation/constants.ts index e96490f077..e1fe315861 100644 --- a/src/components/common/navigation/constants.ts +++ b/src/components/common/navigation/constants.ts @@ -32,6 +32,7 @@ const COMMON_ENV_FALLBACK: Omit devtronManagedLicensingEnabled: false, isResourceRecommendationEnabled: false, featureAskDevtronExpert: false, + forceDockerfileScan: false, } export const ENVIRONMENT_DATA_FALLBACK: EnvironmentDataValuesDTO = { From bd58740ece38c642a74aab0815537a16efc2e6f4 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 31 Mar 2026 16:22:12 +0530 Subject: [PATCH 14/38] chore: version bump --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 84e98eff42..45885ef139 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "1.23.3-beta-14", + "@devtron-labs/devtron-fe-common-lib": "1.23.3-beta-16", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rjsf/core": "^5.13.3", "@rjsf/utils": "^5.13.3", diff --git a/yarn.lock b/yarn.lock index 97343004e4..72fd1cd648 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1729,9 +1729,9 @@ __metadata: languageName: node linkType: hard -"@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-14": - version: 1.23.3-beta-14 - resolution: "@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-14" +"@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-16": + version: 1.23.3-beta-16 + resolution: "@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-16" dependencies: "@codemirror/autocomplete": "npm:6.18.6" "@codemirror/lang-json": "npm:6.0.1" @@ -1785,7 +1785,7 @@ __metadata: react-select: 5.8.0 rxjs: ^7.8.1 yaml: ^2.4.1 - checksum: 10c0/e1875424928736741d26590a311963d94fff11ca4e0ac7113876aea043352206dc72235a122e7f1a7ac1e0eb96a96476a05a2f9821002c29388359fe07d10358 + checksum: 10c0/f1a8301f1f1ee90dde3badc3caf739042143c08b483b1c2cbc7225435acc91f58d58154115f67ba989bbda218be7be286d65bf437d0e20e533aa0a564b2d0a84 languageName: node linkType: hard @@ -5599,7 +5599,7 @@ __metadata: version: 0.0.0-use.local resolution: "dashboard@workspace:." dependencies: - "@devtron-labs/devtron-fe-common-lib": "npm:1.23.3-beta-14" + "@devtron-labs/devtron-fe-common-lib": "npm:1.23.3-beta-16" "@esbuild-plugins/node-globals-polyfill": "npm:0.2.3" "@playwright/test": "npm:^1.32.1" "@rjsf/core": "npm:^5.13.3" From d910c1ed369f9a7fe1121a3fe054604c11ac96cd Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 31 Mar 2026 17:10:39 +0530 Subject: [PATCH 15/38] chore: css fixes --- src/components/app/details/cIDetails/SecurityTab.tsx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/components/app/details/cIDetails/SecurityTab.tsx b/src/components/app/details/cIDetails/SecurityTab.tsx index 408742da5c..42c1064184 100644 --- a/src/components/app/details/cIDetails/SecurityTab.tsx +++ b/src/components/app/details/cIDetails/SecurityTab.tsx @@ -43,13 +43,8 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab }) const renderSecurityScanRecommendation = () => { - if (!forceDockerfileScan && !scanRecommendationsResultResponse?.result?.dockerfileScanEnabled) { - return ( - - ) + if (!forceDockerfileScan && !scanRecommendationsResultResponse?.result?.results?.length) { + return } if (scanRecommendationsResultLoading) { From c7168a6d09b76f079d0350569c3778580d09ec00 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 31 Mar 2026 18:00:17 +0530 Subject: [PATCH 16/38] chore: docker scanned title fix --- src/components/app/details/cIDetails/SecurityTab.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/app/details/cIDetails/SecurityTab.tsx b/src/components/app/details/cIDetails/SecurityTab.tsx index 42c1064184..989cc5a36b 100644 --- a/src/components/app/details/cIDetails/SecurityTab.tsx +++ b/src/components/app/details/cIDetails/SecurityTab.tsx @@ -44,7 +44,7 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab const renderSecurityScanRecommendation = () => { if (!forceDockerfileScan && !scanRecommendationsResultResponse?.result?.results?.length) { - return + return } if (scanRecommendationsResultLoading) { From a8749e4341c692b7d8c5515b0752fa482daa03a8 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 31 Mar 2026 18:20:57 +0530 Subject: [PATCH 17/38] chore: css fixes for loading state --- .../app/details/cIDetails/SecurityTab.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/components/app/details/cIDetails/SecurityTab.tsx b/src/components/app/details/cIDetails/SecurityTab.tsx index 989cc5a36b..8f69f3e32e 100644 --- a/src/components/app/details/cIDetails/SecurityTab.tsx +++ b/src/components/app/details/cIDetails/SecurityTab.tsx @@ -43,10 +43,6 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab }) const renderSecurityScanRecommendation = () => { - if (!forceDockerfileScan && !scanRecommendationsResultResponse?.result?.results?.length) { - return - } - if (scanRecommendationsResultLoading) { return (
@@ -58,6 +54,10 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab ) } + if (!forceDockerfileScan && !scanRecommendationsResultResponse?.result?.results?.length) { + return + } + return (
{getSecurityScanRecommendationTitle()} @@ -114,8 +114,11 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab if (scanResultLoading) { return ( -
- +
+ {renderHeader()} +
+ +
) } From 46b0e6f483f4c001321429a6507aa19b16daeb14 Mon Sep 17 00:00:00 2001 From: Arun Jain Date: Wed, 1 Apr 2026 12:16:18 +0530 Subject: [PATCH 18/38] fix: app env status in expanded state --- src/components/app/list/CellComponent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/app/list/CellComponent.tsx b/src/components/app/list/CellComponent.tsx index 73bb0a1855..35777ab18a 100644 --- a/src/components/app/list/CellComponent.tsx +++ b/src/components/app/list/CellComponent.tsx @@ -30,7 +30,7 @@ export const CellComponent = ({ return (
From 0d30f9cb9509f66e120876dc1a7d22a64d3c9c7b Mon Sep 17 00:00:00 2001 From: shivani170 Date: Wed, 1 Apr 2026 13:39:48 +0530 Subject: [PATCH 19/38] chore: css fixes --- package.json | 2 +- src/components/CIPipelineN/Build.tsx | 9 ++- .../app/details/appDetails/appDetails.type.ts | 2 +- .../DockerfileScanModal.tsx} | 8 +- ...ckerfileScanRecommendation.components.tsx} | 15 ++-- .../DockerfileScanRecommendationBar.tsx} | 11 ++- .../dockerfileScan.utils.tsx} | 0 .../security.scss | 10 ++- .../types.ts | 4 +- .../app/details/cIDetails/SecurityTab.tsx | 79 ++++++++++--------- src/config/constantMessaging.ts | 5 -- yarn.lock | 10 +-- 12 files changed, 83 insertions(+), 72 deletions(-) rename src/components/app/details/cIDetails/{SecurityScanRecommendation/SecurityScanModal.tsx => DockerfileScanRecommendation/DockerfileScanModal.tsx} (95%) rename src/components/app/details/cIDetails/{SecurityScanRecommendation/SecurityScanRecommendations.components.tsx => DockerfileScanRecommendation/DockerfileScanRecommendation.components.tsx} (88%) rename src/components/app/details/cIDetails/{SecurityScanRecommendation/SecrityScanRecommendationBar.tsx => DockerfileScanRecommendation/DockerfileScanRecommendationBar.tsx} (89%) rename src/components/app/details/cIDetails/{SecurityScanRecommendation/SecurityRecommendation.utils.tsx => DockerfileScanRecommendation/dockerfileScan.utils.tsx} (100%) rename src/components/app/details/cIDetails/{SecurityScanRecommendation => DockerfileScanRecommendation}/security.scss (93%) rename src/components/app/details/cIDetails/{SecurityScanRecommendation => DockerfileScanRecommendation}/types.ts (92%) diff --git a/package.json b/package.json index 45885ef139..53df0e8003 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "1.23.3-beta-16", + "@devtron-labs/devtron-fe-common-lib": "1.23.3-beta-17", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rjsf/core": "^5.13.3", "@rjsf/utils": "^5.13.3", diff --git a/src/components/CIPipelineN/Build.tsx b/src/components/CIPipelineN/Build.tsx index e3b8c44079..edac097745 100644 --- a/src/components/CIPipelineN/Build.tsx +++ b/src/components/CIPipelineN/Build.tsx @@ -21,6 +21,7 @@ import { CiPipelineSourceTypeOption, CustomInput, SourceTypeMap, + Icon, } from '@devtron-labs/devtron-fe-common-lib' import { ViewType } from '../../config' import { createWebhookConditionList } from '../ciPipeline/ciPipeline.service' @@ -229,7 +230,9 @@ export const Build = ({ const renderScanner = () => (
- +
+ +

Scan for vulnerabilities

Perform security scan after container image is built.

@@ -244,7 +247,9 @@ export const Build = ({
- +
+ +

Scan for recommendations

diff --git a/src/components/app/details/appDetails/appDetails.type.ts b/src/components/app/details/appDetails/appDetails.type.ts index b1eea20b10..6dab042825 100644 --- a/src/components/app/details/appDetails/appDetails.type.ts +++ b/src/components/app/details/appDetails/appDetails.type.ts @@ -36,7 +36,7 @@ import { AggregatedNodes, UseGetDTAppDetailsReturnType } from '../../types' import { SecurityScanRecommendationRowTypes, SecurityScanRecommendationTableAdditionalProps, -} from '../cIDetails/SecurityScanRecommendation/types' +} from '../cIDetails/DockerfileScanRecommendation/types' export enum AppMetricsTab { Aggregate = 'aggregate', diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanModal.tsx b/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanModal.tsx similarity index 95% rename from src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanModal.tsx rename to src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanModal.tsx index f1bc7fb240..5f6e8a4051 100644 --- a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityScanModal.tsx +++ b/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanModal.tsx @@ -13,8 +13,8 @@ import { VisibleModal, } from '@devtron-labs/devtron-fe-common-lib' -import { SecurityScanRecommendationBar } from './SecrityScanRecommendationBar' -import { getRecommendationRowId, SECURITY_SCAN_RECOMMENDATIONS_TABLE_COLUMNS } from './SecurityRecommendation.utils' +import { getRecommendationRowId, SECURITY_SCAN_RECOMMENDATIONS_TABLE_COLUMNS } from './dockerfileScan.utils' +import { DockerfileScanRecommendationBar } from './DockerfileScanRecommendationBar' import { ExpandRowCallback, SecurityScanRecommendationModalProps, @@ -26,7 +26,7 @@ import './security.scss' const CLOSE_BUTTON_ID = 'security-scan-recommendations-close' -export const SecurityScanModal = ({ +export const DockerfileScanModal = ({ summary, recommendations, handleSecurityScanModal, @@ -125,7 +125,7 @@ export const SecurityScanModal = ({ />

- + { +}: DockerfileScansRecommendationsProps) => { const [showSecurityScanModal, setShowSecurityScanModal] = useState(false) const recommendations: ScanRecommendationsDTO | undefined = scanRecommendationResponse?.result @@ -24,6 +24,7 @@ export const SecurityScansRecommendations = ({ const handleSecurityScanModal = () => { setShowSecurityScanModal((currentState) => !currentState) } + const { error, warning } = severitySummary const hasThreats = error || warning @@ -66,12 +67,12 @@ export const SecurityScansRecommendations = ({
) : (
- {showSecurityScanModal && ( - { +}: DockerfileScanRecommendationBarProps) => { const { error, warning, info } = summary const totalCount = error + warning const shouldShowLastScanTime = @@ -41,7 +40,7 @@ export const SecurityScanRecommendationBar = ({ entities={[ { color: 'var(--Y500)', label: 'Warnings', value: warning || 0 }, { color: 'var(--R500)', label: 'Errors', value: error || 0 }, - { color: 'var(--N500)', label: 'Info', value: info || 0 }, + { color: 'var(--B500)', label: 'Info', value: info || 0 }, ]} labelClassName="fs-13 fw-4 lh-20" countClassName="fs-13 fw-6 lh-20 cn-7" @@ -58,7 +57,7 @@ export const SecurityScanRecommendationBar = ({ Scanned on {dayjs(lastScanTime).format(DATE_TIME_FORMATS.TWELVE_HOURS_FORMAT)}{' '}
- +
) : null}
diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx b/src/components/app/details/cIDetails/DockerfileScanRecommendation/dockerfileScan.utils.tsx similarity index 100% rename from src/components/app/details/cIDetails/SecurityScanRecommendation/SecurityRecommendation.utils.tsx rename to src/components/app/details/cIDetails/DockerfileScanRecommendation/dockerfileScan.utils.tsx diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/security.scss b/src/components/app/details/cIDetails/DockerfileScanRecommendation/security.scss similarity index 93% rename from src/components/app/details/cIDetails/SecurityScanRecommendation/security.scss rename to src/components/app/details/cIDetails/DockerfileScanRecommendation/security.scss index dcb51299c2..1072b9e277 100644 --- a/src/components/app/details/cIDetails/SecurityScanRecommendation/security.scss +++ b/src/components/app/details/cIDetails/DockerfileScanRecommendation/security.scss @@ -1,5 +1,5 @@ .warning { - color: var(--Y500); + color: var(--O600); border: 1px solid var(--progress-O200, #FFBEAD); background: var(--progress-O100, #FFE5DE); color: var(--O600, var(--progress-O600, #E55E39)); @@ -7,6 +7,14 @@ .error { color: var(--R500); + border: 1px solid var(--negative-R200, #FCBCBC); + background: var(--negative-R100, #FDE7E7); +} + +.info { + color: var(--B500); + border: 1px solid var(--B500); + background: var(--B100) } .security-scanner-bar { diff --git a/src/components/app/details/cIDetails/SecurityScanRecommendation/types.ts b/src/components/app/details/cIDetails/DockerfileScanRecommendation/types.ts similarity index 92% rename from src/components/app/details/cIDetails/SecurityScanRecommendation/types.ts rename to src/components/app/details/cIDetails/DockerfileScanRecommendation/types.ts index ba3db1da50..e52379e450 100644 --- a/src/components/app/details/cIDetails/SecurityScanRecommendation/types.ts +++ b/src/components/app/details/cIDetails/DockerfileScanRecommendation/types.ts @@ -2,7 +2,7 @@ import type { MouseEvent } from 'react' import { ResponseType, ScanRecommendationsDTO } from '@devtron-labs/devtron-fe-common-lib' -export interface SecurityScansRecommendationsProps { +export interface DockerfileScansRecommendationsProps { scanRecommendationLoading: boolean scanRecommendationResponse: ResponseType } @@ -23,7 +23,7 @@ export type SecurityScanRecommendationModalProps = { isModalView?: boolean } -export type SecurityScanRecommendationBarProps = Pick< +export type DockerfileScanRecommendationBarProps = Pick< SecurityScanRecommendationModalProps, 'summary' | 'handleSecurityScanModal' | 'isModalView' | 'lastScanTime' > & {} diff --git a/src/components/app/details/cIDetails/SecurityTab.tsx b/src/components/app/details/cIDetails/SecurityTab.tsx index 8f69f3e32e..c34c717933 100644 --- a/src/components/app/details/cIDetails/SecurityTab.tsx +++ b/src/components/app/details/cIDetails/SecurityTab.tsx @@ -1,6 +1,7 @@ import { useParams } from 'react-router-dom' import { + EMPTY_STATE_STATUS, ErrorScreenManager, GenericEmptyState, Progressing, @@ -9,13 +10,11 @@ import { useMainContext, } from '@devtron-labs/devtron-fe-common-lib' -import { ReactComponent as MechanicalOperation } from '@Images/ic-mechanical-operation.svg' import { importComponentFromFELibrary } from '@Components/common' -import { EMPTY_STATE_STATUS } from '@Config/constantMessaging' import { useGetAppSecurityDetails, useGetAppSecurityDetailsRecommendations } from '../appDetails/AppSecurity' -import { getSecurityScanRecommendationTitle } from './SecurityScanRecommendation/SecurityRecommendation.utils' -import { SecurityScansRecommendations } from './SecurityScanRecommendation/SecurityScanRecommendations.components' +import { getSecurityScanRecommendationTitle } from './DockerfileScanRecommendation/dockerfileScan.utils' +import { DockerfileScansRecommendations } from './DockerfileScanRecommendation/DockerfileScanRecommendation.components' import { CIRunningView } from './cIDetails.util' import { SecurityTabType } from './types' @@ -42,45 +41,47 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab buildId: +buildId, }) - const renderSecurityScanRecommendation = () => { + const renderDockerfileScannerContent = () => { if (scanRecommendationsResultLoading) { return ( -
- {getSecurityScanRecommendationTitle()} -
- -
+
+
) } - if (!forceDockerfileScan && !scanRecommendationsResultResponse?.result?.results?.length) { + const dockerfileScanRecommendation = scanRecommendationsResultResponse?.result + + if (!forceDockerfileScan && !dockerfileScanRecommendation?.results?.length) { return } + if (scanRecommendationsResultError) { + return ( +
+ +
+ ) + } + return ( -
- {getSecurityScanRecommendationTitle()} - {scanRecommendationsResultError && ( -
- {scanRecommendationsResultError.code === 404 ? ( - - ) : ( - - )} -
- )} - -
+ ) } + const renderDockerfileScanRecommendation = () => ( +
+ {getSecurityScanRecommendationTitle()} + {renderDockerfileScannerContent()} +
+ ) + const renderHeader = () => (
Security Scan @@ -91,7 +92,7 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab if (['starting', 'running'].includes(normalizedStatus)) { return ( -
+
{renderHeader()}
@@ -100,7 +101,7 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab if (!artifactId) { return ( -
+
{renderHeader()}
+
{renderHeader()} -
+
@@ -125,7 +126,7 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab if (scanResultError) { return ( -
+
{renderHeader()}
@@ -138,9 +139,11 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab } return ( -
- {isEnterprise && renderSecurityScanRecommendation()} - {renderSecurityDetailsCards()} +
+
+ {isEnterprise && renderDockerfileScanRecommendation()} + {renderSecurityDetailsCards()} +
) } diff --git a/src/config/constantMessaging.ts b/src/config/constantMessaging.ts index 65f9ea9503..1c1961f81a 100644 --- a/src/config/constantMessaging.ts +++ b/src/config/constantMessaging.ts @@ -253,11 +253,6 @@ export const EMPTY_STATE_STATUS = { TITLE: 'No pipeline selected', SUBTITLE: 'Please select a pipeline', }, - CI_DETAILS_IMAGE_SCANNED_DISABLED: 'Go to build pipeline configurations and enable ’Scan for vulnerabilities’', - CI_DETAILS_IMAGE_NOT_SCANNED: { - TITLE: 'Image not scanned', - SUBTITLE: 'This build was executed before scanning was enabled for this pipeline.', - }, CD_DETAILS_NO_ENVIRONMENT: { TITLE: 'No environment selected', SUBTITLE: 'Please select an environment to start seeing CD deployments.', diff --git a/yarn.lock b/yarn.lock index 72fd1cd648..36cd77200c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1729,9 +1729,9 @@ __metadata: languageName: node linkType: hard -"@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-16": - version: 1.23.3-beta-16 - resolution: "@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-16" +"@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-17": + version: 1.23.3-beta-17 + resolution: "@devtron-labs/devtron-fe-common-lib@npm:1.23.3-beta-17" dependencies: "@codemirror/autocomplete": "npm:6.18.6" "@codemirror/lang-json": "npm:6.0.1" @@ -1785,7 +1785,7 @@ __metadata: react-select: 5.8.0 rxjs: ^7.8.1 yaml: ^2.4.1 - checksum: 10c0/f1a8301f1f1ee90dde3badc3caf739042143c08b483b1c2cbc7225435acc91f58d58154115f67ba989bbda218be7be286d65bf437d0e20e533aa0a564b2d0a84 + checksum: 10c0/5a3b66d5a242bb95421be4fa89d7dc04a7e17b919e8655f26dad58057046eb8fb5a5a8b9ed82fa7f292b45ac6616abaa4823895c91e01c7c8f646bac5f5f8d73 languageName: node linkType: hard @@ -5599,7 +5599,7 @@ __metadata: version: 0.0.0-use.local resolution: "dashboard@workspace:." dependencies: - "@devtron-labs/devtron-fe-common-lib": "npm:1.23.3-beta-16" + "@devtron-labs/devtron-fe-common-lib": "npm:1.23.3-beta-17" "@esbuild-plugins/node-globals-polyfill": "npm:0.2.3" "@playwright/test": "npm:^1.32.1" "@rjsf/core": "npm:^5.13.3" From 8a0a646152f45a0e35cf14b12322503c4af3ce99 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Wed, 1 Apr 2026 15:41:33 +0530 Subject: [PATCH 20/38] chore: version bump --- package.json | 2 +- .../DockerfileScanRecommendationBar.tsx | 2 +- .../app/details/cIDetails/SecurityTab.tsx | 47 ++++++++----------- yarn.lock | 10 ++-- 4 files changed, 27 insertions(+), 34 deletions(-) diff --git a/package.json b/package.json index 4ed06ba6a2..101303ea9a 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "1.23.4-beta-3", + "@devtron-labs/devtron-fe-common-lib": "1.23.4-beta-4", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rjsf/core": "^5.13.3", "@rjsf/utils": "^5.13.3", diff --git a/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanRecommendationBar.tsx b/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanRecommendationBar.tsx index 36da97c2d6..dd9ee589c4 100644 --- a/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanRecommendationBar.tsx +++ b/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanRecommendationBar.tsx @@ -51,7 +51,7 @@ export const DockerfileScanRecommendationBar = ({
{shouldShowLastScanTime ? ( -
+
diff --git a/src/components/app/details/cIDetails/SecurityTab.tsx b/src/components/app/details/cIDetails/SecurityTab.tsx index c34c717933..2824c66147 100644 --- a/src/components/app/details/cIDetails/SecurityTab.tsx +++ b/src/components/app/details/cIDetails/SecurityTab.tsx @@ -42,7 +42,7 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab }) const renderDockerfileScannerContent = () => { - if (scanRecommendationsResultLoading) { + if (scanRecommendationsResultLoading || scanRecommendationsResultResponse?.result?.status === 0) { return (
@@ -87,57 +87,50 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab Security Scan
) - const renderSecurityDetailsCards = () => { + + const renderSecurityScanContent = () => { const normalizedStatus = status?.toLowerCase() if (['starting', 'running'].includes(normalizedStatus)) { - return ( -
- {renderHeader()} - -
- ) + return } if (!artifactId) { return ( -
- {renderHeader()} -
- -
+
+
) } if (scanResultLoading) { return ( -
- {renderHeader()} -
- -
+
+
) } if (scanResultError) { return ( -
- {renderHeader()} -
- -
+
+
) } - return } + const renderSecurityDetailsCards = () => ( +
+ {renderHeader()} + {renderSecurityScanContent()} +
+ ) + return (
diff --git a/yarn.lock b/yarn.lock index be7bd9d9d4..2be443371c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1729,9 +1729,9 @@ __metadata: languageName: node linkType: hard -"@devtron-labs/devtron-fe-common-lib@npm:1.23.4-beta-3": - version: 1.23.4-beta-3 - resolution: "@devtron-labs/devtron-fe-common-lib@npm:1.23.4-beta-3" +"@devtron-labs/devtron-fe-common-lib@npm:1.23.4-beta-4": + version: 1.23.4-beta-4 + resolution: "@devtron-labs/devtron-fe-common-lib@npm:1.23.4-beta-4" dependencies: "@codemirror/autocomplete": "npm:6.18.6" "@codemirror/lang-json": "npm:6.0.1" @@ -1785,7 +1785,7 @@ __metadata: react-select: 5.8.0 rxjs: ^7.8.1 yaml: ^2.4.1 - checksum: 10c0/f8b3b9580753c4c0fcbf3aa3bdd9fb13e0cf3269df3bd0f39a195b61a3c5b3251f330069d690e3b1d0cd099b6de5e676a9fa39ddcdd34dfa66f9ead112cdec21 + checksum: 10c0/62b45e3bd6fa88d10263b3bf6a0fa17527358ffdb07fb271846d3418e3abdf222275311fb2ef6c88318f0993c5d6288dae1a6e362aae298a82b5321b1adf9adb languageName: node linkType: hard @@ -5599,7 +5599,7 @@ __metadata: version: 0.0.0-use.local resolution: "dashboard@workspace:." dependencies: - "@devtron-labs/devtron-fe-common-lib": "npm:1.23.4-beta-3" + "@devtron-labs/devtron-fe-common-lib": "npm:1.23.4-beta-4" "@esbuild-plugins/node-globals-polyfill": "npm:0.2.3" "@playwright/test": "npm:^1.32.1" "@rjsf/core": "npm:^5.13.3" From 62de89ff69f7b3875d0935813c73e032d181af1e Mon Sep 17 00:00:00 2001 From: shivani170 Date: Wed, 1 Apr 2026 15:45:15 +0530 Subject: [PATCH 21/38] chore: css fixes --- .../app/details/cIDetails/SecurityTab.tsx | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/components/app/details/cIDetails/SecurityTab.tsx b/src/components/app/details/cIDetails/SecurityTab.tsx index 2824c66147..ee4a022be0 100644 --- a/src/components/app/details/cIDetails/SecurityTab.tsx +++ b/src/components/app/details/cIDetails/SecurityTab.tsx @@ -42,6 +42,12 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab }) const renderDockerfileScannerContent = () => { + const dockerfileScanRecommendation = scanRecommendationsResultResponse?.result + + if (!forceDockerfileScan && !dockerfileScanRecommendation?.results?.length) { + return + } + if (scanRecommendationsResultLoading || scanRecommendationsResultResponse?.result?.status === 0) { return (
@@ -50,12 +56,6 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab ) } - const dockerfileScanRecommendation = scanRecommendationsResultResponse?.result - - if (!forceDockerfileScan && !dockerfileScanRecommendation?.results?.length) { - return - } - if (scanRecommendationsResultError) { return (
@@ -91,6 +91,14 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab const renderSecurityScanContent = () => { const normalizedStatus = status?.toLowerCase() + if (scanResultLoading) { + return ( +
+ +
+ ) + } + if (['starting', 'running'].includes(normalizedStatus)) { return } @@ -106,14 +114,6 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab ) } - if (scanResultLoading) { - return ( -
- -
- ) - } - if (scanResultError) { return (
From 8e952d75cafc1410e09f772ae2ff3d7f3651725d Mon Sep 17 00:00:00 2001 From: shivani170 Date: Wed, 1 Apr 2026 17:33:27 +0530 Subject: [PATCH 22/38] chore: polling added in the api --- .../app/details/appDetails/AppSecurity.tsx | 18 +++++++++ ...ockerfileScanRecommendation.components.tsx | 26 +++---------- .../DockerfileScanRecommendationBar.tsx | 2 +- .../DockerfileScanRecommendation/types.ts | 1 - .../app/details/cIDetails/SecurityTab.tsx | 39 +++++++------------ src/config/constantMessaging.ts | 1 - 6 files changed, 40 insertions(+), 47 deletions(-) diff --git a/src/components/app/details/appDetails/AppSecurity.tsx b/src/components/app/details/appDetails/AppSecurity.tsx index 5a87cba932..ffbe10ae5c 100644 --- a/src/components/app/details/appDetails/AppSecurity.tsx +++ b/src/components/app/details/appDetails/AppSecurity.tsx @@ -14,6 +14,8 @@ * limitations under the License. */ +import { useEffect } from 'react' + import { getSecurityScan, getSecurityScanRecommendations, useAsync } from '@devtron-labs/devtron-fe-common-lib' import { @@ -22,6 +24,8 @@ import { UseSecurityRecommendationReturnType, } from './appDetails.type' +const SECURITY_SCAN_RECOMMENDATIONS_POLLING_INTERVAL = 3000 + export const useGetAppSecurityDetails = ({ appId, envId, @@ -53,6 +57,20 @@ export const useGetAppSecurityDetailsRecommendations = ({ reloadScanRecommendationsResult, ] = useAsync(() => getSecurityScanRecommendations({ appId, buildId }), [appId, buildId], !!appId && !!buildId) + useEffect(() => { + if (!appId || !buildId || scanRecommendationsResultResponse?.result?.status !== 0) { + return undefined + } + + const timeoutId = window.setTimeout(() => { + reloadScanRecommendationsResult() + }, SECURITY_SCAN_RECOMMENDATIONS_POLLING_INTERVAL) + + return () => { + window.clearTimeout(timeoutId) + } + }, [appId, buildId, reloadScanRecommendationsResult, scanRecommendationsResultResponse?.result?.status]) + return { scanRecommendationsResultLoading, scanRecommendationsResultResponse, diff --git a/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanRecommendation.components.tsx b/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanRecommendation.components.tsx index 1dfe6024a0..944b1b21c0 100644 --- a/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanRecommendation.components.tsx +++ b/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanRecommendation.components.tsx @@ -1,29 +1,19 @@ import { useState } from 'react' -import { Icon, Progressing, ReportTabEmptyState, ScanRecommendationsDTO } from '@devtron-labs/devtron-fe-common-lib' +import { Icon, ReportTabEmptyState, ScanRecommendationsDTO } from '@devtron-labs/devtron-fe-common-lib' import { DockerfileScanModal } from './DockerfileScanModal' import { DockerfileScanRecommendationBar } from './DockerfileScanRecommendationBar' import { DockerfileScansRecommendationsProps } from './types' -export const DockerfileScansRecommendations = ({ - scanRecommendationLoading, - scanRecommendationResponse, -}: DockerfileScansRecommendationsProps) => { +export const DockerfileScansRecommendations = ({ scanRecommendationResponse }: DockerfileScansRecommendationsProps) => { const [showSecurityScanModal, setShowSecurityScanModal] = useState(false) const recommendations: ScanRecommendationsDTO | undefined = scanRecommendationResponse?.result - if (!recommendations) { - return null - } - const { results, severity_summary: severitySummary, dockerfileScanEnabled } = recommendations - const hasRecommendations = !!results?.length - const handleSecurityScanModal = () => { - setShowSecurityScanModal((currentState) => !currentState) - } + const hasRecommendations = !!results?.length const { error, warning } = severitySummary @@ -38,19 +28,15 @@ export const DockerfileScansRecommendations = ({ ) } - if (scanRecommendationLoading) { - return ( -
- -
- ) + const handleSecurityScanModal = () => { + setShowSecurityScanModal((currentState) => !currentState) } return (
{!hasThreats || !hasRecommendations ? (
diff --git a/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanRecommendationBar.tsx b/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanRecommendationBar.tsx index dd9ee589c4..4060a8c90a 100644 --- a/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanRecommendationBar.tsx +++ b/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanRecommendationBar.tsx @@ -24,7 +24,7 @@ export const DockerfileScanRecommendationBar = ({ return (
diff --git a/src/components/app/details/cIDetails/DockerfileScanRecommendation/types.ts b/src/components/app/details/cIDetails/DockerfileScanRecommendation/types.ts index e52379e450..83a5fc2345 100644 --- a/src/components/app/details/cIDetails/DockerfileScanRecommendation/types.ts +++ b/src/components/app/details/cIDetails/DockerfileScanRecommendation/types.ts @@ -3,7 +3,6 @@ import type { MouseEvent } from 'react' import { ResponseType, ScanRecommendationsDTO } from '@devtron-labs/devtron-fe-common-lib' export interface DockerfileScansRecommendationsProps { - scanRecommendationLoading: boolean scanRecommendationResponse: ResponseType } diff --git a/src/components/app/details/cIDetails/SecurityTab.tsx b/src/components/app/details/cIDetails/SecurityTab.tsx index ee4a022be0..236971b390 100644 --- a/src/components/app/details/cIDetails/SecurityTab.tsx +++ b/src/components/app/details/cIDetails/SecurityTab.tsx @@ -42,43 +42,34 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab }) const renderDockerfileScannerContent = () => { - const dockerfileScanRecommendation = scanRecommendationsResultResponse?.result - - if (!forceDockerfileScan && !dockerfileScanRecommendation?.results?.length) { - return + if (scanRecommendationsResultError) { + return ( + + ) } if (scanRecommendationsResultLoading || scanRecommendationsResultResponse?.result?.status === 0) { return ( -
+
) } - - if (scanRecommendationsResultError) { - return ( -
- -
- ) + if (!forceDockerfileScan && !scanRecommendationsResultResponse?.result?.results) { + return } - return ( - - ) + return } const renderDockerfileScanRecommendation = () => ( -
+
{getSecurityScanRecommendationTitle()} - {renderDockerfileScannerContent()} + +
{renderDockerfileScannerContent()}
) @@ -93,7 +84,7 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab if (scanResultLoading) { return ( -
+
) diff --git a/src/config/constantMessaging.ts b/src/config/constantMessaging.ts index 1c1961f81a..a9dd5e9d09 100644 --- a/src/config/constantMessaging.ts +++ b/src/config/constantMessaging.ts @@ -235,7 +235,6 @@ export const EMPTY_STATE_STATUS = { ConfigureBlobStorage: 'Configure blob storage', NoFilesGenerated: 'No files were generated by the job pipeline.', NoArtifactsGenerated: 'No artifacts generated', - NoArtifactsError: 'Errr..!! We couldn’t build your code.', FailedToFetchArtifacts: 'Fail to find artifacts', FailedToFetchArtifactsError: 'Errr..!! The pipeline execution failed', NoArtifactsFound: 'No new artifacts found', From 46d30dc5398bf8021d979c91d81771e7bc320cd5 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Wed, 1 Apr 2026 17:54:54 +0530 Subject: [PATCH 23/38] chore: css fixes for error state --- src/components/app/details/cIDetails/SecurityTab.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/app/details/cIDetails/SecurityTab.tsx b/src/components/app/details/cIDetails/SecurityTab.tsx index 236971b390..715d9f1f2c 100644 --- a/src/components/app/details/cIDetails/SecurityTab.tsx +++ b/src/components/app/details/cIDetails/SecurityTab.tsx @@ -44,10 +44,12 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab const renderDockerfileScannerContent = () => { if (scanRecommendationsResultError) { return ( - +
+ +
) } From e5f9c9885bf32393a92b91242faf3efccd6bfa6f Mon Sep 17 00:00:00 2001 From: shivani170 Date: Mon, 6 Apr 2026 12:34:08 +0530 Subject: [PATCH 24/38] chore: FORCE_DOCKERFILE_SCAN removed from FE --- .env | 1 - config.md | 5 ++--- src/components/CIPipelineN/Build.tsx | 1 - src/components/CIPipelineN/CIPipeline.tsx | 3 +-- src/components/ciPipeline/ciPipeline.service.ts | 4 +--- src/index.tsx | 1 - 6 files changed, 4 insertions(+), 11 deletions(-) diff --git a/.env b/.env index 074eb18031..2351c2b8a6 100644 --- a/.env +++ b/.env @@ -13,7 +13,6 @@ POSTHOG_ENABLED= POSTHOG_TOKEN= RECOMMEND_SECURITY_SCANNING=false FORCE_SECURITY_SCANNING=false -FORCE_DOCKERFILE_SCAN=false ENABLE_CI_JOB=false HIDE_DISCORD=false DEVTRON_APP_DETAILS_POLLING_INTERVAL=30000 diff --git a/config.md b/config.md index 03b00801af..3907e461f6 100644 --- a/config.md +++ b/config.md @@ -18,9 +18,8 @@ | ENABLE_CHART_SEARCH_IN_HELM_DEPLOY | "true" | Enable chart search in Helm deploy | | ENABLE_EXTERNAL_ARGO_CD | "true" | Enable External Argo CD | | ENABLE_SCOPED_VARIABLES | "false" | For enabling scoped variable from UI, also need to enable it in backend. | -| FORCE_SECURITY_SCANNING | "false" | Force security -scanning | -| FORCE_DOCKERFILE_SCAN | "false" | Force dockerfile scan | +| FORCE_SECURITY_SCANNING | "false" | Force security | +| scanning | | | GA_ENABLED | "true" | Enable Google Analytics (GA) | | GA_TRACKING_ID | G-XXXXXXXX | Google Analytics tracking ID | | GLOBAL_API_TIMEOUT | 60000 | Default timeout for all API requests in DASHBOARD | diff --git a/src/components/CIPipelineN/Build.tsx b/src/components/CIPipelineN/Build.tsx index edac097745..44ae731a14 100644 --- a/src/components/CIPipelineN/Build.tsx +++ b/src/components/CIPipelineN/Build.tsx @@ -257,7 +257,6 @@ export const Build = ({

{ const scanEnabled = window._env_ && (window._env_.RECOMMEND_SECURITY_SCANNING || window._env_.FORCE_SECURITY_SCANNING) - const dockerfileScanEnabled = - window._env_ && (window._env_.RECOMMEND_SECURITY_SCANNING || window._env_.FORCE_DOCKERFILE_SCAN) return { result: { form: { @@ -104,7 +102,7 @@ export function getInitData( preBuildStage: emptyStepsData(), postBuildStage: emptyStepsData(), scanEnabled, - dockerfileScanEnabled: dockerfileScanEnabled, + dockerfileScanEnabled: false, ciPipelineEditable: true, workflowCacheConfig: pipelineMetaConfig.result.workflowCacheConfig ?? null, }, diff --git a/src/index.tsx b/src/index.tsx index 5a66482c3d..8eca26aca9 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -117,7 +117,6 @@ if (!window || !window._env_) { POSTHOG_TOKEN: '', RECOMMEND_SECURITY_SCANNING: false, FORCE_SECURITY_SCANNING: false, - FORCE_DOCKERFILE_SCAN: false, ENABLE_CI_JOB: true, HIDE_DISCORD: true, DEVTRON_APP_DETAILS_POLLING_INTERVAL: 30000, From f1e8569c77a8f29ecd6e33e841d2763a95a65a9b Mon Sep 17 00:00:00 2001 From: shivani170 Date: Mon, 6 Apr 2026 13:12:22 +0530 Subject: [PATCH 25/38] chore: mandatory check removing --- src/components/CIPipelineN/CIPipeline.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/CIPipelineN/CIPipeline.tsx b/src/components/CIPipelineN/CIPipeline.tsx index af622ec4d9..c8cdea5d2d 100644 --- a/src/components/CIPipelineN/CIPipeline.tsx +++ b/src/components/CIPipelineN/CIPipeline.tsx @@ -620,7 +620,6 @@ export default function CIPipeline({ isJobCard || !isSecurityModuleInstalled || formData.scanEnabled || - formData.dockerfileScanEnabled || !window._env_.FORCE_SECURITY_SCANNING if (!scanValidation) { setApiInProgress(false) From e43c1870388fd97430ff9384669c38880a1415b7 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Mon, 6 Apr 2026 13:23:17 +0530 Subject: [PATCH 26/38] chore: dockerfileScanEnabled check fix --- src/components/CIPipelineN/CIPipeline.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/CIPipelineN/CIPipeline.tsx b/src/components/CIPipelineN/CIPipeline.tsx index c8cdea5d2d..c98dd951ea 100644 --- a/src/components/CIPipelineN/CIPipeline.tsx +++ b/src/components/CIPipelineN/CIPipeline.tsx @@ -683,7 +683,7 @@ export default function CIPipeline({ ...formData, materials: _materials, scanEnabled: !isJobCard && isSecurityModuleInstalled ? formData.scanEnabled : false, - dockerfileScanEnabled: !isJobCard && isSecurityModuleInstalled ? formData.dockerfileScanEnabled : false, + dockerfileScanEnabled: formData.dockerfileScanEnabled, }, _ciPipeline, _materials, From 5ac7e9f305e0ea733721e1343e90592da181c6e5 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Mon, 6 Apr 2026 15:44:30 +0530 Subject: [PATCH 27/38] feat: dockerfile scan recommendation removed from dashboard --- src/components/Navigation/constants.ts | 7 + .../app/details/appDetails/appDetails.type.ts | 27 --- .../DockerfileScanModal.tsx | 152 -------------- ...ockerfileScanRecommendation.components.tsx | 73 ------- .../DockerfileScanRecommendationBar.tsx | 67 ------- .../dockerfileScan.utils.tsx | 188 ------------------ .../security.scss | 139 ------------- .../DockerfileScanRecommendation/types.ts | 47 ----- .../app/details/cIDetails/SecurityTab.tsx | 14 +- 9 files changed, 16 insertions(+), 698 deletions(-) delete mode 100644 src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanModal.tsx delete mode 100644 src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanRecommendation.components.tsx delete mode 100644 src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanRecommendationBar.tsx delete mode 100644 src/components/app/details/cIDetails/DockerfileScanRecommendation/dockerfileScan.utils.tsx delete mode 100644 src/components/app/details/cIDetails/DockerfileScanRecommendation/security.scss delete mode 100644 src/components/app/details/cIDetails/DockerfileScanRecommendation/types.ts diff --git a/src/components/Navigation/constants.ts b/src/components/Navigation/constants.ts index 9a23094a32..d5679a9b52 100644 --- a/src/components/Navigation/constants.ts +++ b/src/components/Navigation/constants.ts @@ -146,6 +146,13 @@ const NAVIGATION_LIST: NavigationGroupType[] = [ icon: 'ic-application-group', href: COMMON_URLS.APPLICATION_MANAGEMENT_APPLICATION_GROUP, }, + { + title: 'Audit Logs', + dataTestId: 'click-on-audit-logs', + id: 'application-management-audit-logs', + icon: 'ic-application-group', + href: '/application-management/audit-logs', + }, { title: 'Bulk Edit', dataTestId: 'click-on-bulk-edit', diff --git a/src/components/app/details/appDetails/appDetails.type.ts b/src/components/app/details/appDetails/appDetails.type.ts index 6dab042825..51dfb30ae2 100644 --- a/src/components/app/details/appDetails/appDetails.type.ts +++ b/src/components/app/details/appDetails/appDetails.type.ts @@ -22,21 +22,15 @@ import { AppEnvironment, DeploymentStatusDetailsBreakdownDataType, EnvAppsMetaDTO, - FiltersTypeEnum, OptionType, ResponseType, ScanRecommendationsDTO, ScanResultDTO, SelectPickerProps, ServerErrors, - TableCellComponentProps, } from '@devtron-labs/devtron-fe-common-lib' import { AggregatedNodes, UseGetDTAppDetailsReturnType } from '../../types' -import { - SecurityScanRecommendationRowTypes, - SecurityScanRecommendationTableAdditionalProps, -} from '../cIDetails/DockerfileScanRecommendation/types' export enum AppMetricsTab { Aggregate = 'aggregate', @@ -232,24 +226,3 @@ export type AppEnvSelectorProps = applications: EnvAppsMetaDTO['apps'] environments?: never } - -export type SecurityScanRecommendationColumn = { - label: string - field: string - size: { fixed: number } | null - CellComponent: ( - props: TableCellComponentProps< - SecurityScanRecommendationRowTypes, - FiltersTypeEnum.URL, - SecurityScanRecommendationTableAdditionalProps - >, - ) => JSX.Element | null -} & ( - | { - isSortable: true - comparator: (a: unknown, b: unknown) => number - } - | { - isSortable?: false - } -) diff --git a/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanModal.tsx b/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanModal.tsx deleted file mode 100644 index 5f6e8a4051..0000000000 --- a/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanModal.tsx +++ /dev/null @@ -1,152 +0,0 @@ -import { useCallback, useMemo, useRef } from 'react' - -import { - Button, - ButtonStyleType, - ButtonVariantType, - ComponentSizeType, - FiltersTypeEnum, - Icon, - PaginationEnum, - stopPropagation, - Table, - VisibleModal, -} from '@devtron-labs/devtron-fe-common-lib' - -import { getRecommendationRowId, SECURITY_SCAN_RECOMMENDATIONS_TABLE_COLUMNS } from './dockerfileScan.utils' -import { DockerfileScanRecommendationBar } from './DockerfileScanRecommendationBar' -import { - ExpandRowCallback, - SecurityScanRecommendationModalProps, - SecurityScanRecommendationRowTypes, - SecurityScanRecommendationTableAdditionalProps, -} from './types' - -import './security.scss' - -const CLOSE_BUTTON_ID = 'security-scan-recommendations-close' - -export const DockerfileScanModal = ({ - summary, - recommendations, - handleSecurityScanModal, - lastScanTime, -}: SecurityScanRecommendationModalProps) => { - const expandRowCallbacksRef = useRef>({}) - - const registerExpandRowCallback = useCallback< - SecurityScanRecommendationTableAdditionalProps['registerExpandRowCallback'] - >((rowId, expandRowCallback) => { - if (!expandRowCallback) { - delete expandRowCallbacksRef.current[rowId] - - return - } - - expandRowCallbacksRef.current[rowId] = expandRowCallback - }, []) - - const onRowClick = useCallback( - (row: { id: string; data: SecurityScanRecommendationRowTypes }, isExpandedRow: boolean) => { - const rowId = isExpandedRow ? row.data.parentRowId : row.id - - expandRowCallbacksRef.current[rowId]?.({ - stopPropagation: () => {}, - } as Parameters[0]) - }, - [], - ) - - const rows = useMemo( - () => - recommendations.map((recommendation, index) => { - const rowId = getRecommendationRowId(recommendation, index) - const rowData: SecurityScanRecommendationRowTypes = { - id: rowId, - parentRowId: rowId, - code: recommendation.code, - title: recommendation.title, - level: recommendation.level, - message: recommendation.message, - file: recommendation.file, - line: recommendation.line, - documentationUrl: recommendation.documentationUrl, - codeSnippet: recommendation.codeSnippet, - } - - const expandedRowId = `expanded-row-${rowId}` as const - - return { - id: rowId, - data: rowData, - expandableRows: [ - { - id: expandedRowId, - data: { - ...rowData, - id: expandedRowId, - }, - }, - ], - } - }), - [recommendations], - ) - - const additionalProps = useMemo( - () => ({ - registerExpandRowCallback, - }), - [registerExpandRowCallback], - ) - - return ( - -
-
-

Dockerfile Linting

-
-
- - - id="table__security-scan-recommendations" - columns={SECURITY_SCAN_RECOMMENDATIONS_TABLE_COLUMNS} - rows={rows} - filtersVariant={FiltersTypeEnum.URL} - paginationVariant={PaginationEnum.NOT_PAGINATED} - filter={null} - additionalProps={additionalProps} - emptyStateConfig={{ - noRowsConfig: { - title: 'No recommendations found', - }, - }} - onRowClick={onRowClick} - /> -
-
-
- ) -} diff --git a/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanRecommendation.components.tsx b/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanRecommendation.components.tsx deleted file mode 100644 index 944b1b21c0..0000000000 --- a/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanRecommendation.components.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { useState } from 'react' - -import { Icon, ReportTabEmptyState, ScanRecommendationsDTO } from '@devtron-labs/devtron-fe-common-lib' - -import { DockerfileScanModal } from './DockerfileScanModal' -import { DockerfileScanRecommendationBar } from './DockerfileScanRecommendationBar' -import { DockerfileScansRecommendationsProps } from './types' - -export const DockerfileScansRecommendations = ({ scanRecommendationResponse }: DockerfileScansRecommendationsProps) => { - const [showSecurityScanModal, setShowSecurityScanModal] = useState(false) - - const recommendations: ScanRecommendationsDTO | undefined = scanRecommendationResponse?.result - - const { results, severity_summary: severitySummary, dockerfileScanEnabled } = recommendations - - const hasRecommendations = !!results?.length - - const { error, warning } = severitySummary - - const hasThreats = error || warning - - if (!dockerfileScanEnabled && !hasRecommendations) { - return ( - - ) - } - - const handleSecurityScanModal = () => { - setShowSecurityScanModal((currentState) => !currentState) - } - - return ( -
- {!hasThreats || !hasRecommendations ? ( -
-
-
- Dockerfile Best Practices - Looks good! -
- -
- -
-
- No recommendations suggested -
-
- ) : ( -
- - {showSecurityScanModal && ( - - )} -
- )} -
- ) -} diff --git a/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanRecommendationBar.tsx b/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanRecommendationBar.tsx deleted file mode 100644 index 4060a8c90a..0000000000 --- a/src/components/app/details/cIDetails/DockerfileScanRecommendation/DockerfileScanRecommendationBar.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import dayjs from 'dayjs' - -import { - DATE_TIME_FORMATS, - Icon, - ScannedByToolModal, - SegmentedBarChart, - ZERO_TIME_STRING, -} from '@devtron-labs/devtron-fe-common-lib' - -import { HADOLINT_ICON_LINK } from './dockerfileScan.utils' -import { DockerfileScanRecommendationBarProps } from './types' - -export const DockerfileScanRecommendationBar = ({ - summary, - handleSecurityScanModal, - isModalView = false, - lastScanTime, -}: DockerfileScanRecommendationBarProps) => { - const { error, warning, info } = summary - const totalCount = error + warning - const shouldShowLastScanTime = - isModalView && !!lastScanTime && lastScanTime !== ZERO_TIME_STRING && lastScanTime !== 0 - - return ( -
-
-
-
-
- {!isModalView && Dockerfile Best Practices} - {totalCount} Recommendations -
- -
-
- -
-
- {shouldShowLastScanTime ? ( -
-
- - - Scanned on {dayjs(lastScanTime).format(DATE_TIME_FORMATS.TWELVE_HOURS_FORMAT)}{' '} - -
- -
- ) : null} -
-
- ) -} diff --git a/src/components/app/details/cIDetails/DockerfileScanRecommendation/dockerfileScan.utils.tsx b/src/components/app/details/cIDetails/DockerfileScanRecommendation/dockerfileScan.utils.tsx deleted file mode 100644 index 1d3766c0c5..0000000000 --- a/src/components/app/details/cIDetails/DockerfileScanRecommendation/dockerfileScan.utils.tsx +++ /dev/null @@ -1,188 +0,0 @@ -import { useEffect } from 'react' - -import { - FiltersTypeEnum, - ScannedByToolModal, - stopPropagation, - TableCellComponentProps, -} from '@devtron-labs/devtron-fe-common-lib' - -import { SecurityScanRecommendationColumn } from '../../appDetails/appDetails.type' -import { - RecommendationResult, - RecommendationSnippetLine, - SecurityScanRecommendationRowTypes, - SecurityScanRecommendationTableAdditionalProps, -} from './types' - -import './security.scss' - -const RECOMMENDATION_LEVEL_PRIORITY: Record = { - error: 0, - warning: 1, -} - -const levelComparator = (a: unknown, b: unknown): number => { - const aLevel = String(a || '').toLowerCase() - const bLevel = String(b || '').toLowerCase() - - const aPriority = RECOMMENDATION_LEVEL_PRIORITY[aLevel] ?? Number.MAX_SAFE_INTEGER - const bPriority = RECOMMENDATION_LEVEL_PRIORITY[bLevel] ?? Number.MAX_SAFE_INTEGER - - if (aPriority !== bPriority) { - return aPriority - bPriority - } - - return aLevel.localeCompare(bLevel) -} - -const codeComparator = (a: unknown, b: unknown): number => - String(a || '') - .toLowerCase() - .localeCompare(String(b || '').toLowerCase()) - -export const getRecommendationRowId = (recommendation: RecommendationResult, index: number) => - `recommendation-${recommendation.code || 'code'}-${index}` - -export const getSnippetLines = ({ - codeSnippet, -}: Pick): RecommendationSnippetLine[] => - [...(codeSnippet?.before || []), codeSnippet?.current, ...(codeSnippet?.after || [])].filter(Boolean) - -export const RecommendationCellComponent = ({ - row, - isExpandedRow, - expandRowCallback, - registerExpandRowCallback, -}: TableCellComponentProps< - SecurityScanRecommendationRowTypes, - FiltersTypeEnum.URL, - SecurityScanRecommendationTableAdditionalProps ->) => { - useEffect(() => { - if (isExpandedRow) { - return undefined - } - - registerExpandRowCallback(row.id, expandRowCallback) - - return () => { - registerExpandRowCallback(row.id) - } - }, [expandRowCallback, isExpandedRow, registerExpandRowCallback, row.id]) - - if (!isExpandedRow) { - return
{row.data.title}
- } - - const snippetLines = getSnippetLines(row.data) - - return ( -
-
Dockerfile
- {snippetLines.length ? ( -
-
- {snippetLines.map((snippetLine) => ( -
- - {snippetLine.line || ''} - - {snippetLine.content} -
- ))} -
-
- ) : ( -
No code snippet available.
- )} -
- ) -} - -export const CodeCellComponent = ({ - row, - isExpandedRow, -}: TableCellComponentProps< - SecurityScanRecommendationRowTypes, - FiltersTypeEnum.URL, - SecurityScanRecommendationTableAdditionalProps ->) => { - if (isExpandedRow) { - return null - } - - return ( - - {row.data.code} - - ) -} - -export const LevelCellComponent = ({ - row, - isExpandedRow, -}: TableCellComponentProps< - SecurityScanRecommendationRowTypes, - FiltersTypeEnum.URL, - SecurityScanRecommendationTableAdditionalProps ->) => { - if (isExpandedRow) { - return null - } - - return ( -
- - {row.data.level} - -
- ) -} - -export const SECURITY_SCAN_RECOMMENDATIONS_TABLE_COLUMNS: SecurityScanRecommendationColumn[] = [ - { - label: 'Recommendations', - field: 'title', - size: null, - CellComponent: RecommendationCellComponent, - }, - { - label: 'Level', - field: 'level', - size: { fixed: 100 }, - isSortable: true, - comparator: levelComparator, - CellComponent: LevelCellComponent, - }, - { - label: 'Code', - field: 'code', - size: { fixed: 150 }, - isSortable: true, - comparator: codeComparator, - CellComponent: CodeCellComponent, - }, -] - -export const HADOLINT_ICON_LINK = 'https://cdn.devtron.ai/images/Hadolint.svg' - -export const getSecurityScanRecommendationTitle = () => ( -
-

Dockerfile Linting

- -
-) diff --git a/src/components/app/details/cIDetails/DockerfileScanRecommendation/security.scss b/src/components/app/details/cIDetails/DockerfileScanRecommendation/security.scss deleted file mode 100644 index 1072b9e277..0000000000 --- a/src/components/app/details/cIDetails/DockerfileScanRecommendation/security.scss +++ /dev/null @@ -1,139 +0,0 @@ -.warning { - color: var(--O600); - border: 1px solid var(--progress-O200, #FFBEAD); - background: var(--progress-O100, #FFE5DE); - color: var(--O600, var(--progress-O600, #E55E39)); -} - -.error { - color: var(--R500); - border: 1px solid var(--negative-R200, #FCBCBC); - background: var(--negative-R100, #FDE7E7); -} - -.info { - color: var(--B500); - border: 1px solid var(--B500); - background: var(--B100) -} - -.security-scanner-bar { - &__no-recommendations { - background: radial-gradient(25.91% 100% at 100% 0%, var(--positive-G100, #E6F4EA) 0%, var(--bg-primary, #FFF) 100%); - } - - &__recommendations { - background: radial-gradient(25.91% 100% at 100% 0%, var(--negative-R100, #FDE7E7) 0%, var(--bg-primary, #FFF) 100%); - } -} - -.security-scan-modal { - &__title { - color: var(--N900); - word-break: break-word; - } - - &__code-link { - color: var(--B500); - text-decoration: none; - - &:hover { - text-decoration: underline; - } - } - - &__expanded-row { - background: var(--bg-primary); - min-width: calc(300% + 32px); - border: 1px solid var(--N200); - border-radius: 4px; - overflow: hidden; - } - - &__expanded-header { - display: flex; - justify-content: space-between; - gap: 12px; - background: var(--bg-secondary); - } - - &__expanded-title { - color: var(--N900); - font-size: 13px; - font-weight: 600; - line-height: 20px; - } - - &__snippet-empty { - color: var(--N500); - font-size: 12px; - line-height: 16px; - } - - &__snippet { - overflow: hidden; - background: var(--bg-primary); - } - - &__snippet-lines { - min-width: 100%; - width: max-content; - } - - &__snippet-empty { - padding: 12px; - border: 1px dashed var(--N200); - border-radius: 8px; - background: var(--bg-primary); - } - - &__snippet-line { - display: grid; - grid-template-columns: minmax(24px, max-content) max-content; - gap: 12px; - padding: 8px 12px; - min-width: 100%; - width: max-content; - - &--issue { - color: var(--R500); - } - } - - &__snippet-content { - color: var(--N900); - white-space: pre; - word-break: normal; - overflow-wrap: normal; - } - - .generic-table__row--is-expanded, - .generic-table__row--is-expanded>.generic-table__cell { - background-color: var(--bg-secondary); - } - - .generic-table__row--is-expanded:hover, - .generic-table__row--is-expanded:hover>.generic-table__cell, - .generic-table__row--is-expanded.generic-table__row--active, - .generic-table__row--is-expanded.generic-table__row--active>.generic-table__cell { - background-color: var(--bg-secondary); - } - - .generic-table__row--expanded-row { - background-color: transparent; - } - - .generic-table__row--expanded-row>.generic-table__cell, - .generic-table__row--expanded-row>.expanded-tree-line { - background-color: transparent; - } - - .generic-table__row--expanded-row:hover, - .generic-table__row--expanded-row:hover>.generic-table__cell, - .generic-table__row--expanded-row:hover>.expanded-tree-line, - .generic-table__row--expanded-row.generic-table__row--active, - .generic-table__row--expanded-row.generic-table__row--active>.generic-table__cell, - .generic-table__row--expanded-row.generic-table__row--active>.expanded-tree-line { - background-color: transparent; - } -} \ No newline at end of file diff --git a/src/components/app/details/cIDetails/DockerfileScanRecommendation/types.ts b/src/components/app/details/cIDetails/DockerfileScanRecommendation/types.ts deleted file mode 100644 index 83a5fc2345..0000000000 --- a/src/components/app/details/cIDetails/DockerfileScanRecommendation/types.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { MouseEvent } from 'react' - -import { ResponseType, ScanRecommendationsDTO } from '@devtron-labs/devtron-fe-common-lib' - -export interface DockerfileScansRecommendationsProps { - scanRecommendationResponse: ResponseType -} - -export type RecommendationResult = ScanRecommendationsDTO['results'][number] - -export type RecommendationSnippetLine = { - line?: number - content: string - isIssue?: boolean -} - -export type SecurityScanRecommendationModalProps = { - summary: ScanRecommendationsDTO['severity_summary'] - recommendations: ScanRecommendationsDTO['results'] - handleSecurityScanModal?: () => void - lastScanTime?: ScanRecommendationsDTO['createdOn'] | string - isModalView?: boolean -} - -export type DockerfileScanRecommendationBarProps = Pick< - SecurityScanRecommendationModalProps, - 'summary' | 'handleSecurityScanModal' | 'isModalView' | 'lastScanTime' -> & {} - -export type SecurityScanRecommendationRowTypes = { - id: string - parentRowId: string - code: string - title: string - level: string - message: string - file: string - line: number - documentationUrl: string - codeSnippet: RecommendationResult['codeSnippet'] -} - -export type ExpandRowCallback = (event: MouseEvent) => void - -export type SecurityScanRecommendationTableAdditionalProps = { - registerExpandRowCallback: (rowId: string, expandRowCallback?: ExpandRowCallback) => void -} diff --git a/src/components/app/details/cIDetails/SecurityTab.tsx b/src/components/app/details/cIDetails/SecurityTab.tsx index 715d9f1f2c..fd21f41519 100644 --- a/src/components/app/details/cIDetails/SecurityTab.tsx +++ b/src/components/app/details/cIDetails/SecurityTab.tsx @@ -13,16 +13,20 @@ import { import { importComponentFromFELibrary } from '@Components/common' import { useGetAppSecurityDetails, useGetAppSecurityDetailsRecommendations } from '../appDetails/AppSecurity' -import { getSecurityScanRecommendationTitle } from './DockerfileScanRecommendation/dockerfileScan.utils' -import { DockerfileScansRecommendations } from './DockerfileScanRecommendation/DockerfileScanRecommendation.components' import { CIRunningView } from './cIDetails.util' import { SecurityTabType } from './types' const SecurityModalSidebar = importComponentFromFELibrary('SecurityModalSidebar', null, 'function') +const DockerfileScansRecommendations = importComponentFromFELibrary('DockerfileScansRecommendations') +const getSecurityScanRecommendationTitle = importComponentFromFELibrary( + 'getSecurityScanRecommendationTitle', + null, + 'function', +) export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTabType) => { const { appId, buildId } = useParams<{ appId: string; buildId: string }>() - const { isEnterprise, forceDockerfileScan } = useMainContext() + const { forceDockerfileScan } = useMainContext() const computedAppId = appId ?? appIdFromParent @@ -69,7 +73,7 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab const renderDockerfileScanRecommendation = () => (
- {getSecurityScanRecommendationTitle()} + {!!getSecurityScanRecommendationTitle && getSecurityScanRecommendationTitle()}
{renderDockerfileScannerContent()}
@@ -127,7 +131,7 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab return (
- {isEnterprise && renderDockerfileScanRecommendation()} + {DockerfileScansRecommendations && renderDockerfileScanRecommendation()} {renderSecurityDetailsCards()}
From bf3bf50081f5bb218763e21a41400d21a36f4edc Mon Sep 17 00:00:00 2001 From: shivani170 Date: Mon, 6 Apr 2026 16:21:37 +0530 Subject: [PATCH 28/38] chore: dockerfile scan flag fix --- src/components/CIPipelineN/Build.tsx | 44 ++++++++++++++++------------ 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/components/CIPipelineN/Build.tsx b/src/components/CIPipelineN/Build.tsx index 44ae731a14..84e8703f92 100644 --- a/src/components/CIPipelineN/Build.tsx +++ b/src/components/CIPipelineN/Build.tsx @@ -22,13 +22,13 @@ import { CustomInput, SourceTypeMap, Icon, + useMainContext, } from '@devtron-labs/devtron-fe-common-lib' import { ViewType } from '../../config' import { createWebhookConditionList } from '../ciPipeline/ciPipeline.service' import { SourceMaterials } from '../ciPipeline/SourceMaterials' import { ValidationRules } from '../ciPipeline/validationRules' import { BuildType, WebhookCIProps } from '../ciPipeline/types' -import { ReactComponent as BugScanner } from '../../assets/icons/scanner.svg' import AdvancedConfigOptions from './AdvancedConfigOptions' import { pipelineContext } from '../workflowEditor/workflowEditor' import { getSelectedWebhookEvent } from '@Pages/App/Configurations' @@ -43,6 +43,8 @@ export const Build = ({ appId, isTemplateView, }: BuildType) => { + const { isEnterprise } = useMainContext() + const { formData, setFormData, formDataErrorObj, setFormDataErrorObj } = useContext(pipelineContext) const validationRules = new ValidationRules() const handleSourceChange = (event, gitMaterialId: number, sourceType: string): void => { @@ -245,24 +247,28 @@ export const Build = ({ name="create-build-pipeline-scan-vulnerabilities-toggle" />
-
-
-
- -
-
-

Scan for recommendations

-

- Perform linting scan of your docker file and get recommended optimizations. -

-
- -
+ {isEnterprise && ( + <> +
+
+
+ +
+
+

Scan for recommendations

+

+ Perform linting scan of your docker file and get recommended optimizations. +

+
+ +
+ + )}
) From 9113d581cf266e2ff01e50243c92efdb53c44a5d Mon Sep 17 00:00:00 2001 From: shivani170 Date: Mon, 6 Apr 2026 16:29:44 +0530 Subject: [PATCH 29/38] chore: disabled state implemented for dockerfile toggle --- src/components/CIPipelineN/Build.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/CIPipelineN/Build.tsx b/src/components/CIPipelineN/Build.tsx index 84e8703f92..63c59f0fbf 100644 --- a/src/components/CIPipelineN/Build.tsx +++ b/src/components/CIPipelineN/Build.tsx @@ -43,7 +43,7 @@ export const Build = ({ appId, isTemplateView, }: BuildType) => { - const { isEnterprise } = useMainContext() + const { isEnterprise, forceDockerfileScan } = useMainContext() const { formData, setFormData, formDataErrorObj, setFormDataErrorObj } = useContext(pipelineContext) const validationRules = new ValidationRules() @@ -261,10 +261,12 @@ export const Build = ({

From 399c1505e2b58748c61eee1303d464102978acad Mon Sep 17 00:00:00 2001 From: shivani170 Date: Mon, 6 Apr 2026 17:41:17 +0530 Subject: [PATCH 30/38] chore: initial value of dockerfileScanEnabled fixed --- src/components/CIPipelineN/CIPipeline.tsx | 5 ++++- src/components/ciPipeline/ciPipeline.service.ts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/CIPipelineN/CIPipeline.tsx b/src/components/CIPipelineN/CIPipeline.tsx index c98dd951ea..3094d27c64 100644 --- a/src/components/CIPipelineN/CIPipeline.tsx +++ b/src/components/CIPipelineN/CIPipeline.tsx @@ -48,6 +48,7 @@ import { GenericModal, Button, handleAnalyticsEvent, + useMainContext, } from '@devtron-labs/devtron-fe-common-lib' import Tippy from '@tippyjs/react' import { @@ -122,13 +123,15 @@ export default function CIPipeline({ const [environments, setEnvironments] = useState([]) // NOTE: don't want to show the warning until fetch; therefore true by default const [isBlobStorageConfigured, setIsBlobStorageConfigured] = useState(true) + + const { forceDockerfileScan } = useMainContext() const [formData, setFormData] = useState({ name: '', args: [], materials: [], triggerType: window._env_.DEFAULT_CI_TRIGGER_TYPE_MANUAL ? TriggerType.Manual : TriggerType.Auto, scanEnabled: false, - dockerfileScanEnabled: false, + dockerfileScanEnabled: forceDockerfileScan || false, gitHost: undefined, webhookEvents: [], ciPipelineSourceTypeOptions: [], diff --git a/src/components/ciPipeline/ciPipeline.service.ts b/src/components/ciPipeline/ciPipeline.service.ts index 570aff2241..cca667931d 100644 --- a/src/components/ciPipeline/ciPipeline.service.ts +++ b/src/components/ciPipeline/ciPipeline.service.ts @@ -33,6 +33,7 @@ import { getTemplateAPIRoute, SourceTypeMap, ModuleStatus, + useMainContext, } from '@devtron-labs/devtron-fe-common-lib' import { Routes, ViewType } from '../../config' import { getSourceConfig, getWebhookDataMetaConfig } from '../../services/service' @@ -66,6 +67,8 @@ export function getInitData( isJobCard: boolean, isTemplateView: AppConfigProps['isTemplateView'], ): Promise { + + const { forceDockerfileScan } = useMainContext() return Promise.all([ getCIPipelineNameSuggestion(appId, isTemplateView), getPipelineMetaConfiguration(appId.toString(), includeWebhookData, true, isJobCard, isTemplateView), @@ -102,7 +105,7 @@ export function getInitData( preBuildStage: emptyStepsData(), postBuildStage: emptyStepsData(), scanEnabled, - dockerfileScanEnabled: false, + dockerfileScanEnabled: forceDockerfileScan || false, ciPipelineEditable: true, workflowCacheConfig: pipelineMetaConfig.result.workflowCacheConfig ?? null, }, From 4b5170d871e569a07a19152ef1fbf9a9c138a4f6 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 7 Apr 2026 09:25:13 +0530 Subject: [PATCH 31/38] chore: version bump --- package.json | 2 +- .../CreateCICDPipeline/CreateCICDPipeline.tsx | 5 ++++- .../WorkflowEditor/CreateCICDPipeline/service.ts | 10 +++++++--- src/components/CIPipelineN/CIPipeline.tsx | 2 +- src/components/app/details/appDetails/AppSecurity.tsx | 4 ++-- src/components/app/details/cIDetails/SecurityTab.tsx | 2 +- src/components/ciPipeline/ciPipeline.service.ts | 3 +-- yarn.lock | 10 +++++----- 8 files changed, 22 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 101303ea9a..83be49a2fa 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "1.23.4-beta-4", + "@devtron-labs/devtron-fe-common-lib": "1.23.4-beta-6", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@rjsf/core": "^5.13.3", "@rjsf/utils": "^5.13.3", diff --git a/src/Pages/App/Configurations/WorkflowEditor/CreateCICDPipeline/CreateCICDPipeline.tsx b/src/Pages/App/Configurations/WorkflowEditor/CreateCICDPipeline/CreateCICDPipeline.tsx index 060e5e9f86..40eedcc86b 100644 --- a/src/Pages/App/Configurations/WorkflowEditor/CreateCICDPipeline/CreateCICDPipeline.tsx +++ b/src/Pages/App/Configurations/WorkflowEditor/CreateCICDPipeline/CreateCICDPipeline.tsx @@ -30,6 +30,7 @@ import { ToastManager, ToastVariantType, useAsync, + useMainContext, } from '@devtron-labs/devtron-fe-common-lib' import { saveCIPipeline } from '@Components/ciPipeline/ciPipeline.service' @@ -90,9 +91,11 @@ export const CreateCICDPipeline = ({ // REFS const ciPipelineResRef = useRef<{ appWorkflowId: number; id: number } | null>(null) + const { forceDockerfileScan } = useMainContext() + // ASYNC CALLS const [isCiCdPipelineLoading, ciCdPipelineRes, ciCdPipelineErr, reloadCiCdPipeline, setter] = useAsync( - () => getCICDPipelineInitData(appId, isTemplateView), + () => getCICDPipelineInitData(appId, isTemplateView, forceDockerfileScan), [open, isTemplateView], open, ) diff --git a/src/Pages/App/Configurations/WorkflowEditor/CreateCICDPipeline/service.ts b/src/Pages/App/Configurations/WorkflowEditor/CreateCICDPipeline/service.ts index bc9e39a009..115e004a0b 100644 --- a/src/Pages/App/Configurations/WorkflowEditor/CreateCICDPipeline/service.ts +++ b/src/Pages/App/Configurations/WorkflowEditor/CreateCICDPipeline/service.ts @@ -117,10 +117,10 @@ const getCDInitData = async (appId: string, isTemplateView: boolean): Promise { +const getCIInitData = async (appId: string, isTemplateView: boolean, forceDockerfileScan: boolean) => { const { result: { form, isBlobStorageConfigured, isSecurityModuleInstalled }, - } = await getInitData(appId.toString(), true, false, isTemplateView) + } = await getInitData(appId.toString(), true, false, isTemplateView, forceDockerfileScan) return { ...form, @@ -132,9 +132,13 @@ const getCIInitData = async (appId: string, isTemplateView: boolean) => { export const getCICDPipelineInitData = async ( appId: string, isTemplateView: boolean, + forceDockerfileScan: boolean, ): Promise => { try { - const [ci, cd] = await Promise.all([getCIInitData(appId, isTemplateView), getCDInitData(appId, isTemplateView)]) + const [ci, cd] = await Promise.all([ + getCIInitData(appId, isTemplateView, forceDockerfileScan), + getCDInitData(appId, isTemplateView), + ]) return { ci, cd } } catch (err) { diff --git a/src/components/CIPipelineN/CIPipeline.tsx b/src/components/CIPipelineN/CIPipeline.tsx index 3094d27c64..86ea14598d 100644 --- a/src/components/CIPipelineN/CIPipeline.tsx +++ b/src/components/CIPipelineN/CIPipeline.tsx @@ -472,7 +472,7 @@ export default function CIPipeline({ setIsAdvanced(true) } } else { - const ciPipelineResponse = await getInitData(appId, true, isJobCard, isTemplateView) + const ciPipelineResponse = await getInitData(appId, true, isJobCard, isTemplateView, forceDockerfileScan) if (ciPipelineResponse) { setFormData(ciPipelineResponse.result.form) setSecurityModuleInstalled(ciPipelineResponse.result.isSecurityModuleInstalled) diff --git a/src/components/app/details/appDetails/AppSecurity.tsx b/src/components/app/details/appDetails/AppSecurity.tsx index ffbe10ae5c..ff534e1312 100644 --- a/src/components/app/details/appDetails/AppSecurity.tsx +++ b/src/components/app/details/appDetails/AppSecurity.tsx @@ -24,7 +24,7 @@ import { UseSecurityRecommendationReturnType, } from './appDetails.type' -const SECURITY_SCAN_RECOMMENDATIONS_POLLING_INTERVAL = 3000 +const SECURITY_SCAN_RECOMMENDATIONS_POLLING_INTERVAL = 10000 export const useGetAppSecurityDetails = ({ appId, @@ -58,7 +58,7 @@ export const useGetAppSecurityDetailsRecommendations = ({ ] = useAsync(() => getSecurityScanRecommendations({ appId, buildId }), [appId, buildId], !!appId && !!buildId) useEffect(() => { - if (!appId || !buildId || scanRecommendationsResultResponse?.result?.status !== 0) { + if (!appId || !buildId || !scanRecommendationsResultResponse?.result?.scanEnabled) { return undefined } diff --git a/src/components/app/details/cIDetails/SecurityTab.tsx b/src/components/app/details/cIDetails/SecurityTab.tsx index fd21f41519..fb6590c5c2 100644 --- a/src/components/app/details/cIDetails/SecurityTab.tsx +++ b/src/components/app/details/cIDetails/SecurityTab.tsx @@ -57,7 +57,7 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab ) } - if (scanRecommendationsResultLoading || scanRecommendationsResultResponse?.result?.status === 0) { + if (scanRecommendationsResultLoading) { return (
diff --git a/src/components/ciPipeline/ciPipeline.service.ts b/src/components/ciPipeline/ciPipeline.service.ts index cca667931d..f5d4a989e6 100644 --- a/src/components/ciPipeline/ciPipeline.service.ts +++ b/src/components/ciPipeline/ciPipeline.service.ts @@ -33,7 +33,6 @@ import { getTemplateAPIRoute, SourceTypeMap, ModuleStatus, - useMainContext, } from '@devtron-labs/devtron-fe-common-lib' import { Routes, ViewType } from '../../config' import { getSourceConfig, getWebhookDataMetaConfig } from '../../services/service' @@ -66,9 +65,9 @@ export function getInitData( includeWebhookData: boolean = false, isJobCard: boolean, isTemplateView: AppConfigProps['isTemplateView'], + forceDockerfileScan: boolean ): Promise { - const { forceDockerfileScan } = useMainContext() return Promise.all([ getCIPipelineNameSuggestion(appId, isTemplateView), getPipelineMetaConfiguration(appId.toString(), includeWebhookData, true, isJobCard, isTemplateView), diff --git a/yarn.lock b/yarn.lock index 2be443371c..92bad6aab8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1729,9 +1729,9 @@ __metadata: languageName: node linkType: hard -"@devtron-labs/devtron-fe-common-lib@npm:1.23.4-beta-4": - version: 1.23.4-beta-4 - resolution: "@devtron-labs/devtron-fe-common-lib@npm:1.23.4-beta-4" +"@devtron-labs/devtron-fe-common-lib@npm:1.23.4-beta-6": + version: 1.23.4-beta-6 + resolution: "@devtron-labs/devtron-fe-common-lib@npm:1.23.4-beta-6" dependencies: "@codemirror/autocomplete": "npm:6.18.6" "@codemirror/lang-json": "npm:6.0.1" @@ -1785,7 +1785,7 @@ __metadata: react-select: 5.8.0 rxjs: ^7.8.1 yaml: ^2.4.1 - checksum: 10c0/62b45e3bd6fa88d10263b3bf6a0fa17527358ffdb07fb271846d3418e3abdf222275311fb2ef6c88318f0993c5d6288dae1a6e362aae298a82b5321b1adf9adb + checksum: 10c0/0cb6cfdf7100bedb49ba846f5cadfedc629b3f5805901805785f8895e8147d265289467b136720a00b4a0de3bc19e5a2aa8fbda76242a24bb44fb386fa041c73 languageName: node linkType: hard @@ -5599,7 +5599,7 @@ __metadata: version: 0.0.0-use.local resolution: "dashboard@workspace:." dependencies: - "@devtron-labs/devtron-fe-common-lib": "npm:1.23.4-beta-4" + "@devtron-labs/devtron-fe-common-lib": "npm:1.23.4-beta-6" "@esbuild-plugins/node-globals-polyfill": "npm:0.2.3" "@playwright/test": "npm:^1.32.1" "@rjsf/core": "npm:^5.13.3" From d6eab29a1b13a82e3ccc8d526d9a941a26e72bf8 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 7 Apr 2026 10:24:10 +0530 Subject: [PATCH 32/38] chore: version bump --- src/components/app/details/cIDetails/SecurityTab.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/app/details/cIDetails/SecurityTab.tsx b/src/components/app/details/cIDetails/SecurityTab.tsx index fb6590c5c2..3013001ccf 100644 --- a/src/components/app/details/cIDetails/SecurityTab.tsx +++ b/src/components/app/details/cIDetails/SecurityTab.tsx @@ -57,7 +57,7 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab ) } - if (scanRecommendationsResultLoading) { + if (scanRecommendationsResultLoading || scanRecommendationsResultResponse?.result?.scanEnabled) { return (
From b72b89de16a93ebf91f6425c9b69d7d336470546 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 7 Apr 2026 10:28:41 +0530 Subject: [PATCH 33/38] chore: scanEnabled flag integration --- src/components/app/details/cIDetails/SecurityTab.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/app/details/cIDetails/SecurityTab.tsx b/src/components/app/details/cIDetails/SecurityTab.tsx index 3013001ccf..b6064315e1 100644 --- a/src/components/app/details/cIDetails/SecurityTab.tsx +++ b/src/components/app/details/cIDetails/SecurityTab.tsx @@ -46,6 +46,7 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab }) const renderDockerfileScannerContent = () => { + const scanResult = scanRecommendationsResultResponse?.result if (scanRecommendationsResultError) { return (
@@ -57,14 +58,14 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab ) } - if (scanRecommendationsResultLoading || scanRecommendationsResultResponse?.result?.scanEnabled) { + if ((scanRecommendationsResultLoading || scanResult?.scanEnabled) && !scanResult?.results) { return (
) } - if (!forceDockerfileScan && !scanRecommendationsResultResponse?.result?.results) { + if (!forceDockerfileScan && !scanResult?.results) { return } From edc39e07681f4e859a790c9dff11d945c0c4effa Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 7 Apr 2026 19:00:10 +0530 Subject: [PATCH 34/38] chore: fixed polling for failed state --- .../app/details/appDetails/AppSecurity.tsx | 15 ++++++++++++--- .../app/details/cIDetails/SecurityTab.tsx | 11 ++++++++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/components/app/details/appDetails/AppSecurity.tsx b/src/components/app/details/appDetails/AppSecurity.tsx index ff534e1312..cef6964b5e 100644 --- a/src/components/app/details/appDetails/AppSecurity.tsx +++ b/src/components/app/details/appDetails/AppSecurity.tsx @@ -24,7 +24,7 @@ import { UseSecurityRecommendationReturnType, } from './appDetails.type' -const SECURITY_SCAN_RECOMMENDATIONS_POLLING_INTERVAL = 10000 +const SECURITY_SCAN_RECOMMENDATIONS_POLLING_INTERVAL = 5000 export const useGetAppSecurityDetails = ({ appId, @@ -57,8 +57,17 @@ export const useGetAppSecurityDetailsRecommendations = ({ reloadScanRecommendationsResult, ] = useAsync(() => getSecurityScanRecommendations({ appId, buildId }), [appId, buildId], !!appId && !!buildId) + const recommendationStatus = scanRecommendationsResultResponse?.result?.status + const isDockerfileScanEnabled = scanRecommendationsResultResponse?.result?.scanEnabled + useEffect(() => { - if (!appId || !buildId || !scanRecommendationsResultResponse?.result?.scanEnabled) { + if ( + !appId || + !buildId || + !isDockerfileScanEnabled || + recommendationStatus === 3 || + recommendationStatus !== 0 + ) { return undefined } @@ -69,7 +78,7 @@ export const useGetAppSecurityDetailsRecommendations = ({ return () => { window.clearTimeout(timeoutId) } - }, [appId, buildId, reloadScanRecommendationsResult, scanRecommendationsResultResponse?.result?.status]) + }, [appId, buildId, isDockerfileScanEnabled, recommendationStatus, reloadScanRecommendationsResult]) return { scanRecommendationsResultLoading, diff --git a/src/components/app/details/cIDetails/SecurityTab.tsx b/src/components/app/details/cIDetails/SecurityTab.tsx index b6064315e1..74d8bfd420 100644 --- a/src/components/app/details/cIDetails/SecurityTab.tsx +++ b/src/components/app/details/cIDetails/SecurityTab.tsx @@ -58,6 +58,16 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab ) } + if (scanResult?.status === 3) { + return ( + + ) + } + if ((scanRecommendationsResultLoading || scanResult?.scanEnabled) && !scanResult?.results) { return (
@@ -75,7 +85,6 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab const renderDockerfileScanRecommendation = () => (
{!!getSecurityScanRecommendationTitle && getSecurityScanRecommendationTitle()} -
{renderDockerfileScannerContent()}
) From cf408f32007c0d16cf88ae5dd0d856d0c9305d50 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 7 Apr 2026 19:15:28 +0530 Subject: [PATCH 35/38] chore: fix for disable state --- src/components/app/details/cIDetails/SecurityTab.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/app/details/cIDetails/SecurityTab.tsx b/src/components/app/details/cIDetails/SecurityTab.tsx index 74d8bfd420..a4ccdcb89d 100644 --- a/src/components/app/details/cIDetails/SecurityTab.tsx +++ b/src/components/app/details/cIDetails/SecurityTab.tsx @@ -7,7 +7,6 @@ import { Progressing, ReportTabEmptyState, SecurityDetailsCards, - useMainContext, } from '@devtron-labs/devtron-fe-common-lib' import { importComponentFromFELibrary } from '@Components/common' @@ -26,7 +25,6 @@ const getSecurityScanRecommendationTitle = importComponentFromFELibrary( export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTabType) => { const { appId, buildId } = useParams<{ appId: string; buildId: string }>() - const { forceDockerfileScan } = useMainContext() const computedAppId = appId ?? appIdFromParent @@ -75,7 +73,7 @@ export const SecurityTab = ({ artifactId, status, appIdFromParent }: SecurityTab
) } - if (!forceDockerfileScan && !scanResult?.results) { + if (!scanResult?.scanEnabled && !scanResult?.results) { return } From 93abb0acf750e53fcde5423cde54a2e4f9879d62 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 7 Apr 2026 20:58:47 +0530 Subject: [PATCH 36/38] chore: polling fix --- src/components/app/details/appDetails/AppSecurity.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/components/app/details/appDetails/AppSecurity.tsx b/src/components/app/details/appDetails/AppSecurity.tsx index cef6964b5e..3635fdb6f9 100644 --- a/src/components/app/details/appDetails/AppSecurity.tsx +++ b/src/components/app/details/appDetails/AppSecurity.tsx @@ -61,13 +61,7 @@ export const useGetAppSecurityDetailsRecommendations = ({ const isDockerfileScanEnabled = scanRecommendationsResultResponse?.result?.scanEnabled useEffect(() => { - if ( - !appId || - !buildId || - !isDockerfileScanEnabled || - recommendationStatus === 3 || - recommendationStatus !== 0 - ) { + if (!appId || !buildId || !isDockerfileScanEnabled || recommendationStatus === 3) { return undefined } From 8ca735c4e8686c63d9f062d0218128178eee1a6d Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 7 Apr 2026 21:09:46 +0530 Subject: [PATCH 37/38] chore: fix for polling status 1 --- src/components/app/details/appDetails/AppSecurity.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/app/details/appDetails/AppSecurity.tsx b/src/components/app/details/appDetails/AppSecurity.tsx index 3635fdb6f9..e5b8f081a8 100644 --- a/src/components/app/details/appDetails/AppSecurity.tsx +++ b/src/components/app/details/appDetails/AppSecurity.tsx @@ -61,7 +61,7 @@ export const useGetAppSecurityDetailsRecommendations = ({ const isDockerfileScanEnabled = scanRecommendationsResultResponse?.result?.scanEnabled useEffect(() => { - if (!appId || !buildId || !isDockerfileScanEnabled || recommendationStatus === 3) { + if (!appId || !buildId || !isDockerfileScanEnabled || ![0, 1].includes(recommendationStatus)) { return undefined } From b67d3bc00af877796c37e45e01067154da4aa403 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Wed, 8 Apr 2026 13:32:28 +0530 Subject: [PATCH 38/38] chore: version bump --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 3acb53aa2c..85bc4c2127 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "1.23.4-beta-7", + "@devtron-labs/devtron-fe-common-lib": "1.23.4-pre-2", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@sentry/browser": "7.119.1", "@sentry/integrations": "7.50.0", diff --git a/yarn.lock b/yarn.lock index b1b532201d..6f81025f65 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1738,9 +1738,9 @@ __metadata: languageName: node linkType: hard -"@devtron-labs/devtron-fe-common-lib@npm:1.23.4-beta-7": - version: 1.23.4-beta-7 - resolution: "@devtron-labs/devtron-fe-common-lib@npm:1.23.4-beta-7" +"@devtron-labs/devtron-fe-common-lib@npm:1.23.4-pre-2": + version: 1.23.4-pre-2 + resolution: "@devtron-labs/devtron-fe-common-lib@npm:1.23.4-pre-2" dependencies: "@codemirror/autocomplete": "npm:6.18.6" "@codemirror/lang-json": "npm:6.0.1" @@ -1793,7 +1793,7 @@ __metadata: react-select: 5.8.0 rxjs: ^7.8.1 yaml: ^2.4.1 - checksum: 10c0/1fe6d5a2f6f7ce1acf31d5fdd41aa1722505eb1280669686726f5c95cd43aadb77baa0511827c8473d9d58d691170b8a11b4a40bd7390b677373173b44d337d8 + checksum: 10c0/8006ca150a2b488951828fd0822618c61af3b915ae89c5c3db05916d0bd841af5cf7eb92ae053a6aa8fb1e82b0e9ad4aa96b30071661c8b4f389f36a7b094358 languageName: node linkType: hard @@ -5490,7 +5490,7 @@ __metadata: version: 0.0.0-use.local resolution: "dashboard@workspace:." dependencies: - "@devtron-labs/devtron-fe-common-lib": "npm:1.23.4-beta-7" + "@devtron-labs/devtron-fe-common-lib": "npm:1.23.4-pre-2" "@esbuild-plugins/node-globals-polyfill": "npm:0.2.3" "@playwright/test": "npm:^1.32.1" "@sentry/browser": "npm:7.119.1"