289 lines
7.8 KiB
YAML
289 lines
7.8 KiB
YAML
stages:
|
|
- validate
|
|
- build
|
|
- platform
|
|
- test
|
|
- normalize
|
|
- report
|
|
|
|
default:
|
|
interruptible: true
|
|
retry:
|
|
max: 1
|
|
when:
|
|
- runner_system_failure
|
|
- stuck_or_timeout_failure
|
|
|
|
variables:
|
|
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
|
|
ARTIFACT_ROOT: "$CI_PROJECT_DIR/artifacts"
|
|
KUBE_NAMESPACE: "test-automation"
|
|
ANSIBLE_IMAGE: "$CI_REGISTRY_IMAGE/ansible:$CI_COMMIT_SHA"
|
|
DEMO_TARGET_IMAGE: "$CI_REGISTRY_IMAGE/demo-target:$CI_COMMIT_SHA"
|
|
TARGET_ENVIRONMENT:
|
|
value: "test"
|
|
description: "GitLab environment scope used to select credentials"
|
|
|
|
.python_job:
|
|
image: python:3.13-alpine
|
|
cache:
|
|
key: python-ci-v1
|
|
paths:
|
|
- .cache/pip/
|
|
before_script:
|
|
- python3 -m pip install --disable-pip-version-check -r requirements-ci.txt
|
|
|
|
validate:assets:
|
|
extends: .python_job
|
|
stage: validate
|
|
script:
|
|
- python3 scripts/parse-assets.py assets.yml --expected-environment "$TARGET_ENVIRONMENT" --dotenv artifacts/metadata.env --matrix artifacts/asset-matrix.json
|
|
artifacts:
|
|
expire_in: 30 days
|
|
reports:
|
|
dotenv: artifacts/metadata.env
|
|
paths:
|
|
- artifacts/asset-matrix.json
|
|
|
|
validate:python:
|
|
extends: .python_job
|
|
stage: validate
|
|
script:
|
|
- python3 -m compileall -q scripts methodologies/ansible/scripts methodologies/zap/scripts
|
|
- python3 methodologies/ansible/scripts/normalize.py methodologies/ansible/fixtures/sample-output.json artifacts/normalized/sample-ansible.json
|
|
artifacts:
|
|
expire_in: 7 days
|
|
paths:
|
|
- artifacts/normalized/sample-ansible.json
|
|
|
|
.kaniko_build:
|
|
stage: build
|
|
image:
|
|
name: gcr.io/kaniko-project/executor:v1.23.2-debug
|
|
entrypoint: [""]
|
|
before_script:
|
|
- mkdir -p /kaniko/.docker
|
|
- printf '{"auths":{"%s":{"username":"%s","password":"%s"}}}' "$CI_REGISTRY" "$CI_REGISTRY_USER" "$CI_REGISTRY_PASSWORD" > /kaniko/.docker/config.json
|
|
|
|
build:ansible:
|
|
extends: .kaniko_build
|
|
script:
|
|
- /kaniko/executor --context "$CI_PROJECT_DIR" --dockerfile "$CI_PROJECT_DIR/methodologies/ansible/Dockerfile" --destination "$ANSIBLE_IMAGE"
|
|
rules:
|
|
- if: '$RUN_DEMO == "true"'
|
|
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
|
changes:
|
|
- methodologies/ansible/**/*
|
|
|
|
build:demo-target:
|
|
extends: .kaniko_build
|
|
script:
|
|
- /kaniko/executor --context "$CI_PROJECT_DIR" --dockerfile "$CI_PROJECT_DIR/targets/ubuntu-weak/Dockerfile" --destination "$DEMO_TARGET_IMAGE"
|
|
rules:
|
|
- if: '$RUN_DEMO == "true"'
|
|
|
|
platform:verify:
|
|
stage: platform
|
|
image: alpine:3.20
|
|
needs:
|
|
- validate:assets
|
|
script:
|
|
- test -d /cache/tools
|
|
- df -h /cache/tools
|
|
resource_group: test-automation-platform
|
|
rules:
|
|
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
|
when: manual
|
|
- when: never
|
|
|
|
# Tool jobs are introduced behind explicit opt-in variables. Their Kubernetes
|
|
# Job templates and adapters are added as each tool contract is implemented.
|
|
test:ansible:
|
|
stage: test
|
|
image:
|
|
name: "$CI_REGISTRY_IMAGE/ansible:latest"
|
|
entrypoint: [""]
|
|
needs:
|
|
- validate:assets
|
|
environment:
|
|
name: "$TARGET_ENVIRONMENT"
|
|
action: verify
|
|
script:
|
|
- test -n "${ANSIBLE_SECRET_VARS:-}" || { echo "ANSIBLE_SECRET_VARS file variable is required" >&2; exit 1; }
|
|
- test -f "$ANSIBLE_SECRET_VARS" || { echo "ANSIBLE_SECRET_VARS must be a GitLab file variable" >&2; exit 1; }
|
|
- export ANSIBLE_VARS_FILE="$ANSIBLE_SECRET_VARS"
|
|
- bash methodologies/ansible/run.sh --limit "${ANSIBLE_LIMIT:-all}"
|
|
artifacts:
|
|
when: always
|
|
expire_in: 90 days
|
|
paths:
|
|
- artifacts/raw/ansible/
|
|
- artifacts/normalized/
|
|
- artifacts/rendered/
|
|
rules:
|
|
- if: '$RUN_ANSIBLE == "true"'
|
|
- when: never
|
|
|
|
test:zap:
|
|
stage: test
|
|
image:
|
|
name: zaproxy/zap-stable:latest
|
|
entrypoint: [""]
|
|
needs:
|
|
- validate:assets
|
|
environment:
|
|
name: "$TARGET_ENVIRONMENT"
|
|
action: verify
|
|
script:
|
|
- test -n "${ZAP_TARGET_URL:-}" || { echo "ZAP_TARGET_URL is required" >&2; exit 1; }
|
|
- NORMALIZE_ZAP=false bash methodologies/zap/run.sh "$ZAP_TARGET_URL"
|
|
artifacts:
|
|
when: always
|
|
expire_in: 90 days
|
|
paths:
|
|
- artifacts/raw/zaproxy/
|
|
rules:
|
|
- if: '$RUN_ZAP == "true"'
|
|
- when: never
|
|
|
|
demo:ansible:
|
|
stage: test
|
|
image:
|
|
name: "$ANSIBLE_IMAGE"
|
|
entrypoint: [""]
|
|
services:
|
|
- name: "$DEMO_TARGET_IMAGE"
|
|
alias: demo-target
|
|
needs:
|
|
- build:ansible
|
|
- build:demo-target
|
|
variables:
|
|
DEMO_SSH_PASSWORD: "DemoPassword1!"
|
|
INVENTORY: "$CI_PROJECT_DIR/targets/ubuntu-weak/assets.yml"
|
|
PLAYBOOK: "$CI_PROJECT_DIR/methodologies/ansible/playbooks/demo_target.yml"
|
|
LIMIT: "linux_vms"
|
|
TEST_PROJECT_ID: "demo-ubuntu-weak"
|
|
TEST_PROJECT_NAME: "Ubuntu Weak Target Demonstration"
|
|
TEST_ENVIRONMENT: "test"
|
|
TEST_CUSTOMER: "Internal"
|
|
TEST_LOCATION: "testserv"
|
|
script:
|
|
- |
|
|
python3 <<'PY'
|
|
import socket
|
|
import time
|
|
|
|
for _ in range(60):
|
|
try:
|
|
with socket.create_connection(("demo-target", 22), 2):
|
|
break
|
|
except OSError:
|
|
time.sleep(2)
|
|
else:
|
|
raise SystemExit("SSH target did not become ready")
|
|
PY
|
|
- bash methodologies/ansible/run.sh
|
|
artifacts:
|
|
when: always
|
|
expire_in: 30 days
|
|
paths:
|
|
- artifacts/raw/ansible/
|
|
- artifacts/normalized/
|
|
rules:
|
|
- if: '$RUN_DEMO == "true"'
|
|
|
|
demo:zap:
|
|
stage: test
|
|
image:
|
|
name: zaproxy/zap-stable:latest
|
|
entrypoint: [""]
|
|
services:
|
|
- name: "$DEMO_TARGET_IMAGE"
|
|
alias: demo-target
|
|
needs:
|
|
- build:demo-target
|
|
variables:
|
|
NORMALIZE_ZAP: "false"
|
|
script:
|
|
- |
|
|
python3 <<'PY'
|
|
import socket
|
|
import time
|
|
|
|
for _ in range(60):
|
|
try:
|
|
with socket.create_connection(("demo-target", 443), 2):
|
|
break
|
|
except OSError:
|
|
time.sleep(2)
|
|
else:
|
|
raise SystemExit("HTTPS target did not become ready")
|
|
PY
|
|
- bash methodologies/zap/run.sh https://demo-target
|
|
artifacts:
|
|
when: always
|
|
expire_in: 30 days
|
|
paths:
|
|
- artifacts/raw/zaproxy/
|
|
rules:
|
|
- if: '$RUN_DEMO == "true"'
|
|
|
|
normalize:demo-zap:
|
|
extends: .python_job
|
|
stage: normalize
|
|
needs:
|
|
- job: demo:zap
|
|
artifacts: true
|
|
variables:
|
|
TEST_PROJECT_ID: "demo-ubuntu-weak"
|
|
TEST_PROJECT_NAME: "Ubuntu Weak Target Demonstration"
|
|
TEST_ENVIRONMENT: "test"
|
|
TEST_CUSTOMER: "Internal"
|
|
TEST_LOCATION: "testserv"
|
|
script:
|
|
- python3 methodologies/zap/scripts/normalize.py artifacts/raw/zaproxy/zap.json artifacts/raw/zaproxy/tls.json artifacts/normalized/zaproxy-demo-web.json --target https://demo-target
|
|
artifacts:
|
|
expire_in: 30 days
|
|
paths:
|
|
- artifacts/normalized/zaproxy-demo-web.json
|
|
rules:
|
|
- if: '$RUN_DEMO == "true"'
|
|
|
|
report:demo:
|
|
extends: .python_job
|
|
stage: report
|
|
needs:
|
|
- job: demo:ansible
|
|
artifacts: true
|
|
- job: normalize:demo-zap
|
|
artifacts: true
|
|
script:
|
|
- ANSIBLE_REPORT="$(find artifacts/normalized -name 'ansible-*.json' -print -quit)"
|
|
- test -n "$ANSIBLE_REPORT"
|
|
- python3 scripts/aggregate-reports.py "$ANSIBLE_REPORT" artifacts/normalized/zaproxy-demo-web.json --output artifacts/normalized/combined-demo.json
|
|
- python3 scripts/render-normalized.py artifacts/normalized/combined-demo.json --output-dir artifacts/rendered
|
|
artifacts:
|
|
when: always
|
|
expire_in: 90 days
|
|
paths:
|
|
- artifacts/normalized/combined-demo.json
|
|
- artifacts/rendered/combined-project-scope.md
|
|
- artifacts/rendered/combined-project-scope.html
|
|
- artifacts/rendered/combined-project-scope.pdf
|
|
rules:
|
|
- if: '$RUN_DEMO == "true"'
|
|
|
|
report:sample:
|
|
extends: .python_job
|
|
stage: report
|
|
needs:
|
|
- validate:assets
|
|
- validate:python
|
|
script:
|
|
- python3 scripts/render-normalized.py artifacts/normalized/sample-ansible.json --output-dir artifacts/rendered
|
|
artifacts:
|
|
when: always
|
|
expire_in: 90 days
|
|
paths:
|
|
- artifacts/normalized/
|
|
- artifacts/rendered/ |