all phases complete!
This commit is contained in:
@@ -45,6 +45,33 @@ spec:
|
|||||||
value: "{{workflow.parameters.working-dir}}"
|
value: "{{workflow.parameters.working-dir}}"
|
||||||
- name: fail-on-cvss
|
- name: fail-on-cvss
|
||||||
value: "{{workflow.parameters.fail-on-cvss}}"
|
value: "{{workflow.parameters.fail-on-cvss}}"
|
||||||
|
- name: upload-storage
|
||||||
|
dependencies:
|
||||||
|
- scan-trufflehog
|
||||||
|
- scan-semgrep
|
||||||
|
- scan-kics
|
||||||
|
- scan-socketdev
|
||||||
|
- scan-syft-grype
|
||||||
|
- scan-crossguard
|
||||||
|
template: upload-storage
|
||||||
|
- name: upload-defectdojo
|
||||||
|
dependencies:
|
||||||
|
- scan-trufflehog
|
||||||
|
- scan-semgrep
|
||||||
|
- scan-kics
|
||||||
|
- scan-socketdev
|
||||||
|
- scan-syft-grype
|
||||||
|
- scan-crossguard
|
||||||
|
template: upload-defectdojo
|
||||||
|
- name: enforce-policy
|
||||||
|
dependencies:
|
||||||
|
- upload-storage
|
||||||
|
- upload-defectdojo
|
||||||
|
template: enforce-policy
|
||||||
|
arguments:
|
||||||
|
parameters:
|
||||||
|
- name: fail-on-cvss
|
||||||
|
value: "{{workflow.parameters.fail-on-cvss}}"
|
||||||
- name: sinks-and-enforcement
|
- name: sinks-and-enforcement
|
||||||
dependencies:
|
dependencies:
|
||||||
- scanners
|
- scanners
|
||||||
@@ -132,9 +159,6 @@ spec:
|
|||||||
- name: defectdojo
|
- name: defectdojo
|
||||||
template: scan-crossguard
|
template: scan-crossguard
|
||||||
- name: sinks-and-enforcement
|
- name: sinks-and-enforcement
|
||||||
metadata:
|
|
||||||
annotations:
|
|
||||||
secrets.infisical.com/auto-reload: "true"
|
|
||||||
container:
|
container:
|
||||||
image: alpine:3.20
|
image: alpine:3.20
|
||||||
command:
|
command:
|
||||||
@@ -154,3 +178,9 @@ spec:
|
|||||||
template: scan-syft-grype
|
template: scan-syft-grype
|
||||||
- name: scan-crossguard
|
- name: scan-crossguard
|
||||||
template: scan-crossguard
|
template: scan-crossguard
|
||||||
|
- name: upload-storage
|
||||||
|
template: upload-storage
|
||||||
|
- name: upload-defectdojo
|
||||||
|
template: upload-defectdojo
|
||||||
|
- name: enforce-policy
|
||||||
|
template: enforce-policy
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
{{- if .Values.pipeline.enabled }}
|
||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: ClusterWorkflowTemplate
|
||||||
|
metadata:
|
||||||
|
name: amp-security-pipeline-v1.0.0
|
||||||
|
spec:
|
||||||
|
templates:
|
||||||
|
- name: enforce-policy
|
||||||
|
inputs:
|
||||||
|
parameters:
|
||||||
|
- name: fail-on-cvss
|
||||||
|
container:
|
||||||
|
image: python:3.12-alpine
|
||||||
|
command:
|
||||||
|
- sh
|
||||||
|
- -c
|
||||||
|
args:
|
||||||
|
- |
|
||||||
|
set -eu
|
||||||
|
python - <<'PY'
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
import sys
|
||||||
|
|
||||||
|
threshold = float(os.environ["FAIL_ON_CVSS"])
|
||||||
|
reports_dir = pathlib.Path("/workspace/reports")
|
||||||
|
findings = []
|
||||||
|
|
||||||
|
for report in sorted(reports_dir.iterdir()):
|
||||||
|
if not report.is_file():
|
||||||
|
continue
|
||||||
|
text = report.read_text(errors="ignore")
|
||||||
|
if report.suffix == ".sarif":
|
||||||
|
try:
|
||||||
|
data = json.loads(text)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
continue
|
||||||
|
for run in data.get("runs", []):
|
||||||
|
for result in run.get("results", []):
|
||||||
|
for fix in result.get("properties", {}).get("security-severity", []):
|
||||||
|
pass
|
||||||
|
for level in result.get("properties", {}).values():
|
||||||
|
pass
|
||||||
|
for prop in [result.get("properties", {}), result.get("taxa", [])]:
|
||||||
|
pass
|
||||||
|
for region in result.get("locations", []):
|
||||||
|
pass
|
||||||
|
sev = result.get("properties", {}).get("security-severity")
|
||||||
|
if sev is None:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
score = float(sev)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
continue
|
||||||
|
if score >= threshold:
|
||||||
|
findings.append((report.name, score))
|
||||||
|
elif report.suffix == ".json":
|
||||||
|
try:
|
||||||
|
data = json.loads(text)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
continue
|
||||||
|
if isinstance(data, dict):
|
||||||
|
for item in data.get("findings", data.get("vulnerabilities", [])):
|
||||||
|
score = item.get("cvss") or item.get("score")
|
||||||
|
if score is None:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
score = float(score)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
continue
|
||||||
|
if score >= threshold:
|
||||||
|
findings.append((report.name, score))
|
||||||
|
|
||||||
|
if findings:
|
||||||
|
for name, score in findings:
|
||||||
|
print(f"{name}: CVSS {score} >= {threshold}", file=sys.stderr)
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
print(f"No findings met or exceeded CVSS {threshold}")
|
||||||
|
PY
|
||||||
|
env:
|
||||||
|
- name: FAIL_ON_CVSS
|
||||||
|
value: "{{inputs.parameters.fail-on-cvss}}"
|
||||||
|
volumeMounts:
|
||||||
|
- name: workspace
|
||||||
|
mountPath: /workspace
|
||||||
|
{{- end }}
|
||||||
@@ -6,24 +6,33 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
templates:
|
templates:
|
||||||
- name: scan-crossguard
|
- name: scan-crossguard
|
||||||
metadata:
|
|
||||||
annotations:
|
|
||||||
secrets.infisical.com/auto-reload: "true"
|
|
||||||
initContainers:
|
|
||||||
- name: wait-for-infisical
|
|
||||||
image: alpine:3.20
|
|
||||||
command:
|
|
||||||
- sh
|
|
||||||
- -c
|
|
||||||
args:
|
|
||||||
- until [ -n "${DEFECTDOJO_API_KEY:-}" ]; do sleep 2; done
|
|
||||||
container:
|
container:
|
||||||
image: alpine:3.20
|
image: pulumi/pulumi:3.154.0
|
||||||
|
env:
|
||||||
|
- name: PULUMI_ACCESS_TOKEN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: amp-security-pipeline-secrets
|
||||||
|
key: PULUMI_ACCESS_TOKEN
|
||||||
|
- 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
|
||||||
command:
|
command:
|
||||||
- sh
|
- sh
|
||||||
- -c
|
- -c
|
||||||
args:
|
args:
|
||||||
- mkdir -p /workspace/reports && echo "stub: defectdojo" > /workspace/reports/crossguard.json
|
- |
|
||||||
|
set -eu
|
||||||
|
mkdir -p /workspace/reports
|
||||||
|
cd /workspace
|
||||||
|
pulumi preview --policy-pack ./policy-pack > /workspace/reports/crossguard.json 2>&1 || true
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: workspace
|
- name: workspace
|
||||||
mountPath: /workspace
|
mountPath: /workspace
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
{{- 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 }}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
{{- 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 }}
|
||||||
Reference in New Issue
Block a user