1+ # NightVision DAST in GitLab CI.
2+ #
3+ # Extracts an OpenAPI spec from the Spring source, runs an authenticated DAST scan,
4+ # and emits a GitLab DAST report directly with the NightVision CLI's native
5+ # `nightvision export gitlab` command. No external converter and no Python toolchain:
6+ # the report is produced by the CLI and uploaded as a GitLab `dast` report.
7+ #
8+ # Requires a masked CI/CD variable NIGHTVISION_TOKEN (the demo setup script sets it).
9+
110stages :
2- - sast_scan
3- - dast_scan
4- - convert_sarif_to_gitlab
11+ - extract
12+ - scan
513
614variables :
715 NIGHTVISION_TARGET : javaspringvulny-api-gitlab
816 NIGHTVISION_APP : javaspringvulny-api-gitlab
917 NIGHTVISION_AUTH : javaspringvulny-api-gitlab
10- DOCKER_HOST : tcp://docker:2375/
18+ # Secure docker-in-docker: TLS on 2376 with generated client certs, not the
19+ # unauthenticated tcp://docker:2375 socket.
20+ DOCKER_HOST : tcp://docker:2376
21+ DOCKER_TLS_CERTDIR : " /certs"
22+ DOCKER_TLS_VERIFY : " 1"
23+ DOCKER_CERT_PATH : " /certs/client"
1124 DOCKER_DRIVER : overlay2
12- FF_NETWORK_PER_BUILD : " true" # activate container-to-container networking
25+ FF_NETWORK_PER_BUILD : " true" # container-to-container networking for the scan
1326
14- services :
15- - docker:dind
27+ # Run the full DAST scan only on the default branch or a manual ("Run pipeline")
28+ # trigger, so routine feature pushes do not each launch a scan.
29+ workflow :
30+ rules :
31+ - if : $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
32+ - if : $CI_PIPELINE_SOURCE == "web"
1633
17- sast_scan :
18- stage : sast_scan
34+ extract_spec :
35+ stage : extract
1936 image : ubuntu:latest
20- services :
21- - docker:dind
2237 before_script :
23- - apt-get update && apt-get install -y wget python3-venv python3-docker python3-pip python3 docker-compose curl gcc musl-dev libffi-dev
24- - python3 -m venv venv
25- - source venv/bin/activate
26- - pip3 install requests urllib3 semgrep
38+ - apt-get update && apt-get install -y wget ca-certificates
2739 - wget -c https://downloads.nightvision.net/binaries/latest/nightvision_latest_linux_amd64.tar.gz -O - | tar -xz
2840 - mv nightvision /usr/local/bin/
2941 script :
30- # "Extract API documentation from code"
31- - nightvision swagger extract ./ --lang spring -t ${NIGHTVISION_APP} || true
32- - if [ ! -e openapi-spec.yml ]; then cp backup-openapi-spec.yml openapi-spec.yml; fi
42+ # Extract the OpenAPI spec from the source. On failure, fall back to the
43+ # committed backup spec but log loudly so a real extraction failure is visible
44+ # (it is not silently masked the way `|| true` did).
45+ - |
46+ if nightvision swagger extract ./ --lang spring -t "${NIGHTVISION_APP}"; then
47+ echo "Spec extracted from source."
48+ else
49+ echo "WARNING: 'nightvision swagger extract' failed; using committed backup-openapi-spec.yml." >&2
50+ cp backup-openapi-spec.yml openapi-spec.yml
51+ fi
52+ # Guard on the artifact as well: a zero exit that wrote no spec (e.g. no
53+ # routes extracted) still falls back to the committed backup.
54+ [ -s openapi-spec.yml ] || cp backup-openapi-spec.yml openapi-spec.yml
3355 artifacts :
3456 paths :
3557 - openapi-spec.yml
3658 expire_in : 30 days
3759
3860dast_scan :
39- stage : dast_scan
61+ stage : scan
4062 image : ubuntu:latest
4163 services :
4264 - docker:dind
4365 before_script :
44- - apt-get update && apt-get install -y wget python3-venv python3-docker python3-pip python3 docker-compose curl gcc musl-dev libffi-dev
45- - python3 -m venv venv
46- - source venv/bin/activate
47- - pip3 install requests urllib3 semgrep
66+ - apt-get update && apt-get install -y wget ca-certificates docker.io docker-compose
4867 - wget -c https://downloads.nightvision.net/binaries/latest/nightvision_latest_linux_amd64.tar.gz -O - | tar -xz
4968 - mv nightvision /usr/local/bin/
5069 script :
51- # "Starting the app"
70+ # Fail the job on a scan error even though the scan output is piped to tee:
71+ # without pipefail the pipeline's exit status would be tee's (always 0).
72+ - set -o pipefail
73+ # Start the target application.
5274 - docker-compose up -d
5375 - sleep 15
54- # "Scanning the API"
55- - nightvision scan ${NIGHTVISION_TARGET} -a ${NIGHTVISION_APP} --auth ${NIGHTVISION_AUTH} > scan-results.txt
56- - nightvision export sarif -s "$(head -n 1 scan-results.txt)" --swagger-file openapi-spec.yml
57- # "Getting logs"
58- - for pod in $(docker ps | grep -v 'CONTAINER ID' | grep -v IMAGE | awk '{print $1}'); do docker logs $pod >> test.pod.logs 2>&1; done
76+ # Run the scan and capture its id robustly: extract the scan UUID from the
77+ # output (rather than assuming it is the first line) and fail loudly if none
78+ # is produced, instead of feeding an empty -s to the export.
79+ - nightvision scan "${NIGHTVISION_TARGET}" -a "${NIGHTVISION_APP}" --auth "${NIGHTVISION_AUTH}" | tee scan-results.txt
80+ - SCAN_ID=$(grep -oiE '[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}' scan-results.txt | head -n1)
81+ - ' test -n "${SCAN_ID}" || { echo "ERROR no scan id found in scan output" >&2; exit 1; }'
82+ # Native GitLab DAST report (replaces the former fetch-and-run Python converter).
83+ - nightvision export gitlab -s "${SCAN_ID}" --swagger-file openapi-spec.yml -o gl-dast-report.json
84+ after_script :
85+ # Best-effort container logs for debugging; never fail the job on log capture.
86+ - for pod in $(docker ps -q); do docker logs "$pod" >> test.pod.logs 2>&1 || true; done
5987 artifacts :
88+ reports :
89+ dast : gl-dast-report.json
6090 paths :
91+ - gl-dast-report.json
6192 - test.pod.logs
62- - results.sarif
6393 expire_in : 30 days
6494 dependencies :
65- - sast_scan
66-
67- convert_sarif_to_gitlab :
68- stage : convert_sarif_to_gitlab
69- image : python:3.9
70- script :
71- # Fetch the converter from its single canonical location so this pipeline is
72- # self-contained: a repo that copy-pastes only this YAML (without bundling the
73- # Python script) still produces the report on this final stage. (NV-4418.)
74- # -f makes curl fail loudly on an HTTP error so a missing/moved converter does not
75- # silently produce an empty report.
76- # POC limitation: this pins to the mutable 'main' branch (no tag/SHA or checksum),
77- # so a converter change is picked up automatically. Pin to a tag/commit SHA before
78- # promoting this pattern past the POC.
79- # Phase 1 (NV-4412) replaces this fetch + script with `nightvision export gitlab`.
80- - curl -sSfL https://raw.githubusercontent.com/nvsecurity/nv-public-reference/main/sarif/convert_sarif_to_gitlab.py -o convert_sarif_to_gitlab.py
81- - python3 convert_sarif_to_gitlab.py
82- artifacts :
83- reports :
84- sast : gitlab_security_report.json
85- dependencies :
86- - dast_scan
95+ - extract_spec
0 commit comments