Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
04ca8fe
wip: security recommendation modal
shivani170 Mar 25, 2026
95dac0f
chore: code refactoring
shivani170 Mar 25, 2026
6111941
chore: css fixes
shivani170 Mar 25, 2026
b14586e
chore: scroll fix
shivani170 Mar 25, 2026
014006e
chore: hadolint tool added in the security
shivani170 Mar 26, 2026
fb223f9
chore: css fixes
shivani170 Mar 26, 2026
6c06187
chore: css fixes
shivani170 Mar 26, 2026
dc5151b
chore: css fixes
shivani170 Mar 30, 2026
4c4b8d4
fix: dockerfileScanEnabled FORCE_DOCKERFILE_SCAN cm integration
shivani170 Mar 30, 2026
a0ce557
chore: version bump
shivani170 Mar 31, 2026
3307541
chore: css fixes
shivani170 Mar 31, 2026
0509955
chore: info integration fixes
shivani170 Mar 31, 2026
cc895cf
chore: forceDockerfileScan added
shivani170 Mar 31, 2026
bd58740
chore: version bump
shivani170 Mar 31, 2026
d910c1e
chore: css fixes
shivani170 Mar 31, 2026
c7168a6
chore: docker scanned title fix
shivani170 Mar 31, 2026
a8749e4
chore: css fixes for loading state
shivani170 Mar 31, 2026
46b0e6f
fix: app env status in expanded state
arunjaindev Apr 1, 2026
1a9235d
Merge pull request #3086 from devtron-labs/fix/expanded-app-status
arunjaindev Apr 1, 2026
0d30f9c
chore: css fixes
shivani170 Apr 1, 2026
dbd68a3
Merge branch 'develop' into feat/dockerfile-hadolint-scan
shivani170 Apr 1, 2026
e115173
Merge branch 'main' into feat/dockerfile-hadolint-scan
shivani170 Apr 1, 2026
8a0a646
chore: version bump
shivani170 Apr 1, 2026
c3d3e75
Merge branch 'feat/dockerfile-hadolint-scan' of https://github.com/de…
shivani170 Apr 1, 2026
62de89f
chore: css fixes
shivani170 Apr 1, 2026
8e952d7
chore: polling added in the api
shivani170 Apr 1, 2026
46d30dc
chore: css fixes for error state
shivani170 Apr 1, 2026
e5f9c98
chore: FORCE_DOCKERFILE_SCAN removed from FE
shivani170 Apr 6, 2026
f1e8569
chore: mandatory check removing
shivani170 Apr 6, 2026
e43c187
chore: dockerfileScanEnabled check fix
shivani170 Apr 6, 2026
5ac7e9f
feat: dockerfile scan recommendation removed from dashboard
shivani170 Apr 6, 2026
bf3bf50
chore: dockerfile scan flag fix
shivani170 Apr 6, 2026
9113d58
chore: disabled state implemented for dockerfile toggle
shivani170 Apr 6, 2026
399c150
chore: initial value of dockerfileScanEnabled fixed
shivani170 Apr 6, 2026
4b5170d
chore: version bump
shivani170 Apr 7, 2026
d6eab29
chore: version bump
shivani170 Apr 7, 2026
b72b89d
chore: scanEnabled flag integration
shivani170 Apr 7, 2026
67962cb
Merge branch 'develop' into feat/dockerfile-hadolint-scan
shivani170 Apr 7, 2026
edc39e0
chore: fixed polling for failed state
shivani170 Apr 7, 2026
cf408f3
chore: fix for disable state
shivani170 Apr 7, 2026
93abb0a
chore: polling fix
shivani170 Apr 7, 2026
8ca735c
chore: fix for polling status 1
shivani170 Apr 7, 2026
b67d3bc
chore: version bump
shivani170 Apr 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion config.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +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_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 |
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"homepage": "/dashboard",
"dependencies": {
"@devtron-labs/devtron-fe-common-lib": "1.23.4-pre-1",
"@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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
ToastManager,
ToastVariantType,
useAsync,
useMainContext,
} from '@devtron-labs/devtron-fe-common-lib'

import { saveCIPipeline } from '@Components/ciPipeline/ciPipeline.service'
Expand Down Expand Up @@ -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,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,10 @@ const getCDInitData = async (appId: string, isTemplateView: boolean): Promise<Cr
}
}

const getCIInitData = async (appId: string, isTemplateView: boolean) => {
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,
Expand All @@ -132,9 +132,13 @@ const getCIInitData = async (appId: string, isTemplateView: boolean) => {
export const getCICDPipelineInitData = async (
appId: string,
isTemplateView: boolean,
forceDockerfileScan: boolean,
): Promise<CreateCICDPipelineData> => {
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) {
Expand Down
97 changes: 62 additions & 35 deletions src/components/CIPipelineN/Build.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ import {
CiPipelineSourceTypeOption,
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 BugScanner from '../../assets/icons/scanner.svg?react'
import AdvancedConfigOptions from './AdvancedConfigOptions'
import { pipelineContext } from '../workflowEditor/workflowEditor'
import { getSelectedWebhookEvent } from '@Pages/App/Configurations'
Expand All @@ -42,6 +43,8 @@ export const Build = ({
appId,
isTemplateView,
}: BuildType) => {
const { isEnterprise, forceDockerfileScan } = useMainContext()

const { formData, setFormData, formDataErrorObj, setFormDataErrorObj } = useContext(pipelineContext)
const validationRules = new ValidationRules()
const handleSourceChange = (event, gitMaterialId: number, sourceType: string): void => {
Expand Down Expand Up @@ -166,6 +169,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,
Expand Down Expand Up @@ -206,53 +215,71 @@ export const Build = ({

const renderPipelineName = () => {
return (
<label className="form__row">
<CustomInput
name="name"
label="Pipeline Name"
disabled={!!ciPipeline?.id}
placeholder="e.g. my-first-pipeline"
type="text"
value={formData.name}
onChange={handlePipelineName}
required
error={formDataErrorObj.name && !formDataErrorObj.name.isValid && formDataErrorObj.name.message}
/>
</label>
<CustomInput
name="name"
label="Pipeline Name"
disabled={!!ciPipeline?.id}
placeholder="e.g. my-first-pipeline"
type="text"
value={formData.name}
onChange={handlePipelineName}
required
error={formDataErrorObj.name && !formDataErrorObj.name.isValid && formDataErrorObj.name.message}
/>
)
}

const renderScanner = () => (
<>
<hr />
<div>
<div
className="en-2 bw-1 br-4 pt-12 pb-12 pl-16 pr-12"
style={{ display: 'grid', gridTemplateColumns: '52px auto 32px' }}
>
<BugScanner />
<div>
<p className="fs-13 lh-20 fw-6 cn-9 mb-4">Scan for vulnerabilities</p>
<p className="fs-13 lh-18 mb-0 fs-12">Perform security scan after container image is built.</p>
</div>
<DTSwitch
isDisabled={window._env_.FORCE_SECURITY_SCANNING && formData.scanEnabled}
ariaLabel="Toggle scan for security vulnerabilities"
isChecked={formData.scanEnabled}
onChange={handleScanToggle}
name="create-build-pipeline-scan-vulnerabilities-toggle"
/>
<div className="en-2 bw-1 br-4 p-16 flexbox-col dc__gap-16">
<div style={{ display: 'grid', gridTemplateColumns: '52px auto 32px' }}>
<div className="flex icon-dim-40 scan-icon-wrapper">
<Icon name="ic-bg-scan" size={30} color={null} />
</div>
<div>
<p className="fs-13 lh-20 fw-6 cn-9 mb-4">Scan for vulnerabilities</p>
<p className="fs-13 lh-18 mb-0 fs-12">Perform security scan after container image is built.</p>
</div>
<DTSwitch
isDisabled={window._env_.FORCE_SECURITY_SCANNING && formData.scanEnabled}
ariaLabel="Toggle scan for security vulnerabilities"
isChecked={formData.scanEnabled}
onChange={handleScanToggle}
name="create-build-pipeline-scan-vulnerabilities-toggle"
/>
</div>
</>
{isEnterprise && (
<>
<div className="dc__border-bottom dc__secondary" />
<div style={{ display: 'grid', gridTemplateColumns: '52px auto 32px' }}>
<div className="icon-dim-40 scan-icon-wrapper flex">
<Icon name="ic-bg-docker-scanner" size={30} color={null} />
</div>
<div>
<p className="fs-13 lh-20 fw-6 cn-9 mb-4">Scan for recommendations</p>
<p className="fs-13 lh-18 mb-0 fs-12">
Perform linting scan of your docker file and get recommended optimizations.
</p>
</div>
<DTSwitch
isDisabled={forceDockerfileScan}
ariaLabel="Toggle scan for security vulnerabilities"
isChecked={!!forceDockerfileScan || !!formData.dockerfileScanEnabled }
onChange={handleDockerfileScanToggle}
name="create-build-pipeline-dockerfile-scan-toggle"
tooltipContent={forceDockerfileScan ? 'Dockerfile scanning is enforced across all pipelines in the organization.' : null}
/>
</div>
</>
)}
</div>
)

return pageState === ViewType.LOADING.toString() ? (
<div style={{ minHeight: '200px' }} className="flex">
<Progressing pageLoader />
</div>
) : (
<div className="p-20 ci-scrollable-content">
<div className="p-20 ci-scrollable-content flexbox-col dc__gap-16">
{renderBasicCI()}
{!isJobView && isAdvanced && (
<>
Expand Down
12 changes: 10 additions & 2 deletions src/components/CIPipelineN/CIPipeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
GenericModal,
Button,
handleAnalyticsEvent,
useMainContext,
} from '@devtron-labs/devtron-fe-common-lib'
import Tippy from '@tippyjs/react'
import {
Expand Down Expand Up @@ -121,12 +122,15 @@
const [environments, setEnvironments] = useState<EnvironmentWithSelectPickerType[]>([])
// 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<PipelineFormType>({
name: '',
args: [],
materials: [],
triggerType: window._env_.DEFAULT_CI_TRIGGER_TYPE_MANUAL ? TriggerType.Manual : TriggerType.Auto,
scanEnabled: false,
dockerfileScanEnabled: forceDockerfileScan || false,
gitHost: undefined,
webhookEvents: [],
ciPipelineSourceTypeOptions: [],
Expand Down Expand Up @@ -467,7 +471,7 @@
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)
Expand Down Expand Up @@ -615,7 +619,10 @@
validateStage(BuildStageVariable.Build, formData)
validateStage(BuildStageVariable.PostBuild, formData)
const scanValidation =
isJobCard || !isSecurityModuleInstalled || formData.scanEnabled || !window._env_.FORCE_SECURITY_SCANNING
isJobCard ||
!isSecurityModuleInstalled ||
formData.scanEnabled ||
!window._env_.FORCE_SECURITY_SCANNING

Check warning on line 625 in src/components/CIPipelineN/CIPipeline.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `globalThis` over `window`.

See more on https://sonarcloud.io/project/issues?id=devtron-labs_dashboard&issues=AZ1IiHceMsoPwz0Fh_42&open=AZ1IiHceMsoPwz0Fh_42&pullRequest=3084
if (!scanValidation) {
setApiInProgress(false)
ToastManager.showToast({
Expand Down Expand Up @@ -678,6 +685,7 @@
...formData,
materials: _materials,
scanEnabled: !isJobCard && isSecurityModuleInstalled ? formData.scanEnabled : false,
dockerfileScanEnabled: formData.dockerfileScanEnabled,
},
_ciPipeline,
Comment thread
shivani170 marked this conversation as resolved.
_materials,
Expand Down
50 changes: 47 additions & 3 deletions src/components/app/details/appDetails/AppSecurity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,17 @@
* limitations under the License.
*/

import { getSecurityScan, useAsync } from '@devtron-labs/devtron-fe-common-lib'
import { useEffect } from 'react'

import { UseGetAppSecurityDetailsProps, UseGetAppSecurityDetailsReturnType } from './appDetails.type'
import { getSecurityScan, getSecurityScanRecommendations, useAsync } from '@devtron-labs/devtron-fe-common-lib'

import {
UseGetAppSecurityDetailsProps,
UseGetAppSecurityDetailsReturnType,
UseSecurityRecommendationReturnType,
} from './appDetails.type'

const SECURITY_SCAN_RECOMMENDATIONS_POLLING_INTERVAL = 5000

export const useGetAppSecurityDetails = ({
appId,
Expand All @@ -26,7 +34,7 @@
}: UseGetAppSecurityDetailsProps): UseGetAppSecurityDetailsReturnType => {
const [scanResultLoading, scanResultResponse, scanResultError, reloadScanResult] = useAsync(
() => getSecurityScan({ appId, envId, artifactId, installedAppId }),
[appId, envId, installedAppId],
[appId, envId, artifactId, installedAppId],
!!appId || !!installedAppId,
)

Expand All @@ -37,3 +45,39 @@
reloadScanResult,
}
}

export const useGetAppSecurityDetailsRecommendations = ({
appId,
buildId,
}: UseGetAppSecurityDetailsProps): UseSecurityRecommendationReturnType => {
const [
scanRecommendationsResultLoading,
scanRecommendationsResultResponse,
scanRecommendationsResultError,
reloadScanRecommendationsResult,
] = useAsync(() => getSecurityScanRecommendations({ appId, buildId }), [appId, buildId], !!appId && !!buildId)

const recommendationStatus = scanRecommendationsResultResponse?.result?.status
const isDockerfileScanEnabled = scanRecommendationsResultResponse?.result?.scanEnabled

useEffect(() => {
if (!appId || !buildId || !isDockerfileScanEnabled || ![0, 1].includes(recommendationStatus)) {
return undefined
}

const timeoutId = window.setTimeout(() => {

Check warning on line 68 in src/components/app/details/appDetails/AppSecurity.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `globalThis` over `window`.

See more on https://sonarcloud.io/project/issues?id=devtron-labs_dashboard&issues=AZ1I7sEI3JBpSDiAt9Q2&open=AZ1I7sEI3JBpSDiAt9Q2&pullRequest=3084
reloadScanRecommendationsResult()
}, SECURITY_SCAN_RECOMMENDATIONS_POLLING_INTERVAL)

return () => {
window.clearTimeout(timeoutId)

Check warning on line 73 in src/components/app/details/appDetails/AppSecurity.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `globalThis` over `window`.

See more on https://sonarcloud.io/project/issues?id=devtron-labs_dashboard&issues=AZ1I7sEI3JBpSDiAt9Q3&open=AZ1I7sEI3JBpSDiAt9Q3&pullRequest=3084
}
}, [appId, buildId, isDockerfileScanEnabled, recommendationStatus, reloadScanRecommendationsResult])

return {
scanRecommendationsResultLoading,
scanRecommendationsResultResponse,
scanRecommendationsResultError,
reloadScanRecommendationsResult,
}
}
8 changes: 8 additions & 0 deletions src/components/app/details/appDetails/appDetails.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
EnvAppsMetaDTO,
OptionType,
ResponseType,
ScanRecommendationsDTO,
ScanResultDTO,
SelectPickerProps,
ServerErrors,
Expand Down Expand Up @@ -176,13 +177,20 @@ export interface UseGetAppSecurityDetailsProps {
envId?: number
installedAppId?: number
artifactId?: number
buildId?: number
}
export interface UseGetAppSecurityDetailsReturnType {
scanResultLoading: boolean
scanResultResponse: ResponseType<ScanResultDTO>
scanResultError: ServerErrors
reloadScanResult: () => void
}
export interface UseSecurityRecommendationReturnType {
scanRecommendationsResultLoading: boolean
scanRecommendationsResultResponse: ResponseType<ScanRecommendationsDTO>
scanRecommendationsResultError: ServerErrors
reloadScanRecommendationsResult: () => void
}

export enum HibernationModalTypes {
HIBERNATE = 'hibernate',
Expand Down
Loading
Loading