remove embedded python that moved to defectdojo and enforce policy and change to standalone typescript
This commit is contained in:
@@ -1,66 +0,0 @@
|
||||
{{- if .Values.pipeline.enabled }}
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ClusterWorkflowTemplate
|
||||
metadata:
|
||||
name: amp-security-pipeline-v1.0.0
|
||||
spec:
|
||||
templates:
|
||||
- name: upload-defectdojo
|
||||
container:
|
||||
image: python:3.12-alpine
|
||||
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:
|
||||
- sh
|
||||
- -c
|
||||
args:
|
||||
- |
|
||||
set -eu
|
||||
python - <<'PY'
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import urllib.request
|
||||
|
||||
base_url = os.environ["DEFECTDOJO_URL"].rstrip("/")
|
||||
api_token = os.environ["DEFECTDOJO_API_TOKEN"]
|
||||
product_name = os.environ.get("DEFECTDOJO_PRODUCT_NAME", "agentguard-ci")
|
||||
scan_map = {
|
||||
".sarif": "SARIF",
|
||||
".json": "Generic Findings Import",
|
||||
}
|
||||
reports_dir = pathlib.Path("/workspace/reports")
|
||||
for report in sorted(reports_dir.iterdir()):
|
||||
if not report.is_file():
|
||||
continue
|
||||
scan_type = scan_map.get(report.suffix)
|
||||
if not scan_type:
|
||||
continue
|
||||
req = urllib.request.Request(
|
||||
f"{base_url}/api/v2/import-scan/",
|
||||
data=json.dumps({
|
||||
"scan_type": scan_type,
|
||||
"product_name": product_name,
|
||||
"file_name": report.name,
|
||||
}).encode(),
|
||||
headers={
|
||||
"Authorization": f"Token {api_token}",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method="POST",
|
||||
)
|
||||
urllib.request.urlopen(req)
|
||||
PY
|
||||
volumeMounts:
|
||||
- name: workspace
|
||||
mountPath: /workspace
|
||||
{{- end }}
|
||||
@@ -1,45 +0,0 @@
|
||||
{{- if .Values.pipeline.enabled }}
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ClusterWorkflowTemplate
|
||||
metadata:
|
||||
name: amp-security-pipeline-v1.0.0
|
||||
spec:
|
||||
templates:
|
||||
- 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
|
||||
{{- end }}
|
||||
@@ -0,0 +1,85 @@
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
|
||||
export function checkReports(reportsDir: string, threshold: number): { name: string; score: number }[] {
|
||||
const findings: { name: string; score: number }[] = [];
|
||||
if (!fs.existsSync(reportsDir)) return findings;
|
||||
|
||||
const files = fs.readdirSync(reportsDir).sort();
|
||||
|
||||
for (const file of files) {
|
||||
const fullPath = path.join(reportsDir, file);
|
||||
if (!fs.statSync(fullPath).isFile()) continue;
|
||||
|
||||
const text = fs.readFileSync(fullPath, 'utf-8');
|
||||
let data: any;
|
||||
try {
|
||||
data = JSON.parse(text);
|
||||
} catch (e) {
|
||||
console.error(`Error parsing ${file}: Invalid JSON`);
|
||||
process.exitCode = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (file.endsWith('.sarif')) {
|
||||
const runs = data.runs || [];
|
||||
for (const run of runs) {
|
||||
const results = run.results || [];
|
||||
for (const result of results) {
|
||||
const sev = result.properties?.['security-severity'];
|
||||
if (sev === undefined) continue;
|
||||
|
||||
const score = parseFloat(sev);
|
||||
if (isNaN(score)) continue;
|
||||
|
||||
if (score >= threshold) {
|
||||
findings.push({ name: file, score });
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (file.endsWith('.json')) {
|
||||
const items = data.findings || data.vulnerabilities || [];
|
||||
for (const item of items) {
|
||||
const rawScore = item.cvss || item.score;
|
||||
if (rawScore === undefined) continue;
|
||||
|
||||
const score = parseFloat(rawScore);
|
||||
if (isNaN(score)) continue;
|
||||
|
||||
if (score >= threshold) {
|
||||
findings.push({ name: file, score });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return findings;
|
||||
}
|
||||
|
||||
// Ensure the code runs when executed directly
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
||||
const thresholdStr = process.env.FAIL_ON_CVSS;
|
||||
if (!thresholdStr) {
|
||||
console.error("FAIL_ON_CVSS environment variable is required.");
|
||||
process.exit(1);
|
||||
}
|
||||
const threshold = parseFloat(thresholdStr);
|
||||
if (isNaN(threshold)) {
|
||||
console.error("FAIL_ON_CVSS must be a number.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const reportsDir = "/workspace/reports";
|
||||
const findings = checkReports(reportsDir, threshold);
|
||||
|
||||
if (findings.length > 0) {
|
||||
for (const finding of findings) {
|
||||
console.error(`${finding.name}: CVSS ${finding.score} >= ${threshold}`);
|
||||
}
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log(`No findings met or exceeded CVSS ${threshold}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import * 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(/\/$/, "");
|
||||
const apiToken = process.env.DEFECTDOJO_API_TOKEN;
|
||||
const productName = process.env.DEFECTDOJO_PRODUCT_NAME || "agentguard-ci";
|
||||
|
||||
if (!baseUrl || !apiToken) {
|
||||
console.error("DEFECTDOJO_URL and DEFECTDOJO_API_TOKEN must be set.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const scanMap: Record<string, string> = {
|
||||
".sarif": "SARIF",
|
||||
".json": "Generic Findings Import",
|
||||
};
|
||||
|
||||
const reportsDir = "/workspace/reports";
|
||||
if (!fs.existsSync(reportsDir)) {
|
||||
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;
|
||||
|
||||
console.log(`Uploading ${file} as ${scanType}...`);
|
||||
|
||||
try {
|
||||
const response = await fetch(`${baseUrl}/api/v2/import-scan/`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Authorization": `Token ${apiToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
scan_type: scanType,
|
||||
product_name: productName,
|
||||
file_name: file,
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text();
|
||||
console.error(`Failed to upload ${file}: ${response.status} ${response.statusText} - ${text}`);
|
||||
process.exitCode = 1;
|
||||
} else {
|
||||
console.log(`Successfully uploaded ${file}`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`Network error uploading ${file}:`, e);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
||||
uploadReports();
|
||||
}
|
||||
Reference in New Issue
Block a user