From 5bdf3fe1144ee410d88c12e98a4f7c973adbfe5a Mon Sep 17 00:00:00 2001 From: ada Date: Mon, 20 Apr 2026 18:06:58 -0600 Subject: [PATCH] add working dir as input to step --- helm/templates/_enforce-policy.yaml | 31 +++---- helm/templates/_scan-kics.yaml | 43 +++++----- helm/templates/_scan-semgrep.yaml | 31 +++---- helm/templates/_scan-socketdev.yaml | 43 +++++----- helm/templates/_scan-syft-grype.yaml | 33 ++++---- helm/templates/_scan-trufflehog.yaml | 31 +++---- helm/templates/_upload-defectdojo.yaml | 57 ++++++++----- helm/templates/_upload-storage.yaml | 84 +++++++++++-------- tools/src/upload-defectdojo.ts | 112 ++++++++++++++++--------- 9 files changed, 271 insertions(+), 194 deletions(-) diff --git a/helm/templates/_enforce-policy.yaml b/helm/templates/_enforce-policy.yaml index 7b3fc16..98188c4 100644 --- a/helm/templates/_enforce-policy.yaml +++ b/helm/templates/_enforce-policy.yaml @@ -1,17 +1,18 @@ -{{- define "template.enforce-policy" }} +{{- define "template.enforce-policy" -}} - name: enforce-policy - inputs: - parameters: - - name: fail-on-cvss - container: - image: agentguard-tools:latest - command: - - node - - /app/dist/enforce-policy.js - env: - - name: FAIL_ON_CVSS - value: "{{inputs.parameters.fail-on-cvss}}" - volumeMounts: - - name: workspace - mountPath: /workspace + inputs: + parameters: + - name: fail-on-cvss + container: + image: "{{ .Values.pipeline.toolsImage.repository }}:{{ .Values.pipeline.toolsImage.tag }}" + imagePullPolicy: {{ .Values.pipeline.toolsImage.pullPolicy }} + command: + - node + - /app/dist/enforce-policy.js + env: + - name: FAIL_ON_CVSS + value: {{ `{{inputs.parameters.fail-on-cvss}}` | quote }} + volumeMounts: + - name: workspace + mountPath: /workspace {{- end }} diff --git a/helm/templates/_scan-kics.yaml b/helm/templates/_scan-kics.yaml index f764431..63ae252 100644 --- a/helm/templates/_scan-kics.yaml +++ b/helm/templates/_scan-kics.yaml @@ -1,22 +1,25 @@ -{{- define "template.scan-kics" }} +{{- define "template.scan-kics" -}} - name: scan-kics - container: - image: checkmarx/kics:1.7.14 - command: - - sh - - -c - args: - - | - set -eu - mkdir -p /workspace/reports - kics scan -p /workspace -o /workspace/reports --report-formats sarif,json --output-name kics || true - if [ -f /workspace/reports/kics.sarif ]; then - exit 0 - fi - if [ -f /workspace/reports/kics.json ]; then - cp /workspace/reports/kics.json /workspace/reports/kics.sarif - fi - volumeMounts: - - name: workspace - mountPath: /workspace + inputs: + parameters: + - name: working-dir + container: + image: {{ .Values.images.kics | quote }} + command: + - sh + - -c + args: + - | + set -eu + mkdir -p /workspace/reports + kics scan -p "/workspace/{{ `{{inputs.parameters.working-dir}}` }}" -o /workspace/reports --report-formats sarif,json --output-name kics || true + if [ -f /workspace/reports/kics.sarif ]; then + exit 0 + fi + if [ -f /workspace/reports/kics.json ]; then + cp /workspace/reports/kics.json /workspace/reports/kics.sarif + fi + volumeMounts: + - name: workspace + mountPath: /workspace {{- end }} diff --git a/helm/templates/_scan-semgrep.yaml b/helm/templates/_scan-semgrep.yaml index e7c482e..aaf241b 100644 --- a/helm/templates/_scan-semgrep.yaml +++ b/helm/templates/_scan-semgrep.yaml @@ -1,16 +1,19 @@ -{{- define "template.scan-semgrep" }} +{{- define "template.scan-semgrep" -}} - name: scan-semgrep - container: - image: returntocorp/semgrep:1.85.0 - command: - - sh - - -c - args: - - | - set -eu - mkdir -p /workspace/reports - semgrep scan --config auto --sarif --output /workspace/reports/semgrep.sarif /workspace || true - volumeMounts: - - name: workspace - mountPath: /workspace + inputs: + parameters: + - name: working-dir + container: + image: {{ .Values.images.semgrep | quote }} + command: + - sh + - -c + args: + - | + set -eu + mkdir -p /workspace/reports + semgrep scan --config auto --sarif --output /workspace/reports/semgrep.sarif "/workspace/{{ `{{inputs.parameters.working-dir}}` }}" || true + volumeMounts: + - name: workspace + mountPath: /workspace {{- end }} diff --git a/helm/templates/_scan-socketdev.yaml b/helm/templates/_scan-socketdev.yaml index f5c1cb5..7d564fc 100644 --- a/helm/templates/_scan-socketdev.yaml +++ b/helm/templates/_scan-socketdev.yaml @@ -1,22 +1,25 @@ -{{- define "template.scan-socketdev" }} +{{- define "template.scan-socketdev" -}} - name: scan-socketdev - container: - image: socketdev/socketcli:latest - env: - - name: SOCKET_DEV_API_KEY - valueFrom: - secretKeyRef: - name: amp-security-pipeline-secrets - key: SOCKET_DEV_API_KEY - command: - - sh - - -c - args: - - | - set -eu - mkdir -p /workspace/reports - socketdev scan /workspace --format json --output /workspace/reports/socketdev.json || true - volumeMounts: - - name: workspace - mountPath: /workspace + inputs: + parameters: + - name: working-dir + container: + image: {{ .Values.images.socketdev | quote }} + env: + - name: SOCKET_DEV_API_KEY + valueFrom: + secretKeyRef: + name: amp-security-pipeline-secrets + key: SOCKET_DEV_API_KEY + command: + - sh + - -c + args: + - | + set -eu + mkdir -p /workspace/reports + socketdev scan "/workspace/{{ `{{inputs.parameters.working-dir}}` }}" --format json --output /workspace/reports/socketdev.json || true + volumeMounts: + - name: workspace + mountPath: /workspace {{- end }} diff --git a/helm/templates/_scan-syft-grype.yaml b/helm/templates/_scan-syft-grype.yaml index 4595f68..79cff47 100644 --- a/helm/templates/_scan-syft-grype.yaml +++ b/helm/templates/_scan-syft-grype.yaml @@ -1,17 +1,20 @@ -{{- define "template.scan-syft-grype" }} +{{- define "template.scan-syft-grype" -}} - name: scan-syft-grype - container: - image: anchore/syft:latest - command: - - sh - - -c - args: - - | - set -eu - mkdir -p /workspace/reports - syft scan dir:/workspace -o cyclonedx-json=/workspace/reports/sbom.json || true - grype sbom:/workspace/reports/sbom.json -o sarif=/workspace/reports/grype.sarif || true - volumeMounts: - - name: workspace - mountPath: /workspace + inputs: + parameters: + - name: working-dir + container: + image: {{ .Values.images.syftGrype | quote }} + command: + - sh + - -c + args: + - | + set -eu + mkdir -p /workspace/reports + syft scan dir:/workspace/{{ `{{inputs.parameters.working-dir}}` }} -o cyclonedx-json=/workspace/reports/sbom.json || true + grype sbom:/workspace/reports/sbom.json -o sarif=/workspace/reports/grype.sarif || true + volumeMounts: + - name: workspace + mountPath: /workspace {{- end }} diff --git a/helm/templates/_scan-trufflehog.yaml b/helm/templates/_scan-trufflehog.yaml index 28ac35d..ee7b820 100644 --- a/helm/templates/_scan-trufflehog.yaml +++ b/helm/templates/_scan-trufflehog.yaml @@ -1,16 +1,19 @@ -{{- define "template.scan-trufflehog" }} +{{- define "template.scan-trufflehog" -}} - name: scan-trufflehog - container: - image: trufflesecurity/trufflehog:latest - command: - - sh - - -c - args: - - | - set -eu - mkdir -p /workspace/reports - trufflehog filesystem /workspace --json > /workspace/reports/trufflehog.json || true - volumeMounts: - - name: workspace - mountPath: /workspace + inputs: + parameters: + - name: working-dir + container: + image: {{ .Values.images.trufflehog | quote }} + command: + - sh + - -c + args: + - | + set -eu + mkdir -p /workspace/reports + trufflehog filesystem "/workspace/{{ `{{inputs.parameters.working-dir}}` }}" --json > /workspace/reports/trufflehog.json || true + volumeMounts: + - name: workspace + mountPath: /workspace {{- end }} diff --git a/helm/templates/_upload-defectdojo.yaml b/helm/templates/_upload-defectdojo.yaml index 28edc0e..f3f93d8 100644 --- a/helm/templates/_upload-defectdojo.yaml +++ b/helm/templates/_upload-defectdojo.yaml @@ -1,22 +1,39 @@ -{{- define "template.upload-defectdojo" }} +{{- define "template.upload-defectdojo" -}} - name: upload-defectdojo - container: - image: agentguard-tools:latest - env: - - name: DEFECTDOJO_URL - valueFrom: - secretKeyRef: - name: amp-security-pipeline-secrets - key: DEFECTDOJO_URL - - name: DEFECTDOJO_API_TOKEN - valueFrom: - secretKeyRef: - name: amp-security-pipeline-secrets - key: DEFECTDOJO_API_TOKEN - command: - - node - - /app/dist/upload-defectdojo.js - volumeMounts: - - name: workspace - mountPath: /workspace + container: + image: "{{ .Values.pipeline.toolsImage.repository }}:{{ .Values.pipeline.toolsImage.tag }}" + imagePullPolicy: {{ .Values.pipeline.toolsImage.pullPolicy }} + env: + - name: DEFECTDOJO_URL + valueFrom: + secretKeyRef: + name: amp-security-pipeline-secrets + key: DEFECTDOJO_URL + - name: DEFECTDOJO_API_TOKEN + valueFrom: + secretKeyRef: + name: amp-security-pipeline-secrets + key: DEFECTDOJO_API_TOKEN + - name: DEFECTDOJO_PRODUCT_TYPE_NAME + value: {{ .Values.defectdojo.productTypeName | quote }} + - name: DEFECTDOJO_PRODUCT_NAME + value: {{ .Values.defectdojo.productName | quote }} + - name: DEFECTDOJO_ENGAGEMENT_NAME + value: {{ .Values.defectdojo.engagementName | quote }} + - name: DEFECTDOJO_MINIMUM_SEVERITY + value: {{ .Values.defectdojo.minimumSeverity | quote }} + - name: DEFECTDOJO_ACTIVE + value: {{ .Values.defectdojo.active | quote }} + - name: DEFECTDOJO_VERIFIED + value: {{ .Values.defectdojo.verified | quote }} + - name: DEFECTDOJO_CLOSE_OLD_FINDINGS + value: {{ .Values.defectdojo.closeOldFindings | quote }} + - name: DEFECTDOJO_AUTO_CREATE_CONTEXT + value: {{ .Values.defectdojo.autoCreateContext | quote }} + command: + - node + - /app/dist/upload-defectdojo.js + volumeMounts: + - name: workspace + mountPath: /workspace {{- end }} diff --git a/helm/templates/_upload-storage.yaml b/helm/templates/_upload-storage.yaml index 5e3e735..fa9bb6e 100644 --- a/helm/templates/_upload-storage.yaml +++ b/helm/templates/_upload-storage.yaml @@ -1,39 +1,49 @@ -{{- define "template.upload-storage" }} +{{- define "template.upload-storage" -}} - name: upload-storage - container: - image: amazon/aws-cli:2.15.40 - env: - - name: AWS_ACCESS_KEY_ID - valueFrom: - secretKeyRef: - name: amp-security-pipeline-secrets - key: AWS_ACCESS_KEY_ID - - name: AWS_SECRET_ACCESS_KEY - valueFrom: - secretKeyRef: - name: amp-security-pipeline-secrets - key: AWS_SECRET_ACCESS_KEY - - name: MINIO_ROOT_USER - valueFrom: - secretKeyRef: - name: amp-security-pipeline-secrets - key: MINIO_ROOT_USER - - name: MINIO_ROOT_PASSWORD - valueFrom: - secretKeyRef: - name: amp-security-pipeline-secrets - key: MINIO_ROOT_PASSWORD - command: - - sh - - -c - args: - - | - set -eu - repo_name="${REPO_NAME:-repo}" - commit_sha="${GIT_COMMIT_SHA:-unknown}" - report_date="$(date -u +%F)" - aws s3 sync /workspace/reports "s3://${REPORTS_BUCKET:-security-reports}/${repo_name}/${report_date}/${commit_sha}/" - volumeMounts: - - name: workspace - mountPath: /workspace + container: + image: {{ .Values.images.awsCli | quote }} + env: + - name: AWS_ACCESS_KEY_ID + valueFrom: + secretKeyRef: + name: amp-security-pipeline-secrets + key: AWS_ACCESS_KEY_ID + - name: AWS_SECRET_ACCESS_KEY + valueFrom: + secretKeyRef: + name: amp-security-pipeline-secrets + key: AWS_SECRET_ACCESS_KEY + - name: MINIO_ROOT_USER + valueFrom: + secretKeyRef: + name: amp-security-pipeline-secrets + key: MINIO_ROOT_USER + - name: MINIO_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: amp-security-pipeline-secrets + key: MINIO_ROOT_PASSWORD + - name: REPORTS_BUCKET + value: {{ .Values.storage.reportsBucket | quote }} + - name: REPO_NAME + value: {{ .Values.pipeline.repoName | quote }} + - name: STORAGE_ENDPOINT + value: {{ .Values.storage.endpoint | quote }} + command: + - sh + - -c + args: + - | + set -eu + commit_sha="${GIT_COMMIT_SHA:-unknown}" + report_date="$(date -u +%F)" + sync_target="s3://${REPORTS_BUCKET}/${REPO_NAME}/${report_date}/${commit_sha}/" + if [ -n "${STORAGE_ENDPOINT}" ]; then + aws --endpoint-url "${STORAGE_ENDPOINT}" s3 sync /workspace/reports "${sync_target}" + else + aws s3 sync /workspace/reports "${sync_target}" + fi + volumeMounts: + - name: workspace + mountPath: /workspace {{- end }} diff --git a/tools/src/upload-defectdojo.ts b/tools/src/upload-defectdojo.ts index a8839a4..1b7df95 100644 --- a/tools/src/upload-defectdojo.ts +++ b/tools/src/upload-defectdojo.ts @@ -1,68 +1,102 @@ -import * as fs from 'node:fs'; +import { promises as fs } from 'node:fs'; import * as path from 'node:path'; import { fileURLToPath } from 'node:url'; -export async function uploadReports() { - const baseUrl = (process.env.DEFECTDOJO_URL || "").replace(/\/$/, ""); +function resolveScanType(fileName: string): string | undefined { + if (fileName.endsWith('.sarif')) { + return 'SARIF'; + } + + if (fileName === 'generic-findings.json') { + return 'Generic Findings Import'; + } + + return undefined; +} + +export async function uploadReports(): Promise { + const baseUrl = (process.env.DEFECTDOJO_URL || '').replace(/\/$/, ''); const apiToken = process.env.DEFECTDOJO_API_TOKEN; - const productName = process.env.DEFECTDOJO_PRODUCT_NAME || "agentguard-ci"; - + const productTypeName = process.env.DEFECTDOJO_PRODUCT_TYPE_NAME || 'Homelab Security'; + const productName = process.env.DEFECTDOJO_PRODUCT_NAME || 'agentguard-ci'; + const engagementName = process.env.DEFECTDOJO_ENGAGEMENT_NAME || 'Default Pipeline'; + const minimumSeverity = process.env.DEFECTDOJO_MINIMUM_SEVERITY || 'Info'; + const active = process.env.DEFECTDOJO_ACTIVE || 'true'; + const verified = process.env.DEFECTDOJO_VERIFIED || 'true'; + const closeOldFindings = process.env.DEFECTDOJO_CLOSE_OLD_FINDINGS || 'false'; + const autoCreateContext = process.env.DEFECTDOJO_AUTO_CREATE_CONTEXT || 'true'; + if (!baseUrl || !apiToken) { - console.error("DEFECTDOJO_URL and DEFECTDOJO_API_TOKEN must be set."); + console.error('DEFECTDOJO_URL and DEFECTDOJO_API_TOKEN must be set.'); process.exit(1); } - const scanMap: Record = { - ".sarif": "SARIF", - ".json": "Generic Findings Import", - }; + const reportsDir = '/workspace/reports'; + let fileNames: string[]; - const reportsDir = "/workspace/reports"; - if (!fs.existsSync(reportsDir)) { - console.log("No reports directory found."); + try { + fileNames = (await fs.readdir(reportsDir)).sort(); + } catch { + console.log('No reports directory found.'); return; } - const files = fs.readdirSync(reportsDir).sort(); - - for (const file of files) { - const fullPath = path.join(reportsDir, file); - if (!fs.statSync(fullPath).isFile()) continue; - - const ext = path.extname(file); - const scanType = scanMap[ext]; - if (!scanType) continue; + for (const fileName of fileNames) { + const fullPath = path.join(reportsDir, fileName); + const stats = await fs.stat(fullPath); + if (!stats.isFile()) { + continue; + } + + const scanType = resolveScanType(fileName); + if (!scanType) { + console.log(`Skipping ${fileName}: no DefectDojo importer is configured for this file yet.`); + continue; + } + + const reportContents = await fs.readFile(fullPath); + const form = new FormData(); + form.append('scan_type', scanType); + form.append('product_type_name', productTypeName); + form.append('product_name', productName); + form.append('engagement_name', engagementName); + form.append('test_title', fileName); + form.append('minimum_severity', minimumSeverity); + form.append('active', active); + form.append('verified', verified); + form.append('close_old_findings', closeOldFindings); + form.append('auto_create_context', autoCreateContext); + form.append('file', new Blob([reportContents]), fileName); + + console.log(`Uploading ${fileName} to DefectDojo as ${scanType}...`); - console.log(`Uploading ${file} as ${scanType}...`); - try { - const response = await fetch(`${baseUrl}/api/v2/import-scan/`, { - method: "POST", + const response = await fetch(`${baseUrl}/api/v2/reimport-scan/`, { + method: 'POST', headers: { - "Authorization": `Token ${apiToken}`, - "Content-Type": "application/json", + Authorization: `Token ${apiToken}`, }, - body: JSON.stringify({ - scan_type: scanType, - product_name: productName, - file_name: file, - }) + body: form, }); if (!response.ok) { const text = await response.text(); - console.error(`Failed to upload ${file}: ${response.status} ${response.statusText} - ${text}`); + console.error(`Failed to upload ${fileName}: ${response.status} ${response.statusText} - ${text}`); process.exitCode = 1; - } else { - console.log(`Successfully uploaded ${file}`); + continue; } - } catch (e) { - console.error(`Network error uploading ${file}:`, e); + + console.log(`Successfully uploaded ${fileName}`); + } catch (error) { + console.error(`Network error uploading ${fileName}:`, error); process.exitCode = 1; } } } if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) { - uploadReports(); + uploadReports().catch((error: unknown) => { + console.error('Unexpected upload failure:', error); + process.exit(1); + }); }