Skip to content

Merge pull request #5470 from makr-code/hardening/replication-empty-r… #289

Merge pull request #5470 from makr-code/hardening/replication-empty-r…

Merge pull request #5470 from makr-code/hardening/replication-empty-r… #289

name: Security — OWASP ZAP DAST
# Phase 4.1: OWASP ZAP Dynamic Application Security Testing in CI
# Runs ZAP baseline scan against the ThemisDB REST API in a Docker container.
# HIGH/CRITICAL findings fail the CI job; MEDIUM findings are reported but
# non-blocking (pending tuning).
on:
push:
branches:
- main
- develop
paths:
- 'src/server/**'
- 'src/auth/**'
- 'src/governance/**'
- '.github/workflows/security-dast-ci.yml'
- 'scripts/zap-context.xml'
pull_request:
branches:
- main
- develop
paths:
- 'src/server/**'
- 'src/auth/**'
- 'src/governance/**'
- '.github/workflows/security-dast-ci.yml'
schedule:
# Weekly on Sunday 02:00 UTC
- cron: '0 2 * * 0'
workflow_dispatch: {}
concurrency:
group: dast-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
# ──────────────────────────────────────────────────────────────────────────
# DAST baseline scan
# ──────────────────────────────────────────────────────────────────────────
dast-scan:
name: OWASP ZAP Baseline DAST
runs-on: ubuntu-22.04
timeout-minutes: 45
permissions:
contents: read
security-events: write
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# ── Start ThemisDB in a container ─────────────────────────────────────
- name: Start ThemisDB (production mode, self-signed TLS)
run: |
docker compose -f docker-compose.yml up -d themisdb
# Wait for the REST API to become healthy (max 60s)
for i in $(seq 1 30); do
if curl -sk https://localhost:8443/v1/health | grep -q "ok"; then
echo "ThemisDB is ready"
break
fi
echo "Waiting for ThemisDB... ($i/30)"
sleep 2
done
# ── Create ZAP context configuration ──────────────────────────────────
- name: Create ZAP context file
run: |
mkdir -p scripts
if [ ! -f scripts/zap-context.xml ]; then
cat > scripts/zap-context.xml << 'ZAPXML'
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configuration>
<context>
<name>ThemisDB API</name>
<desc>ThemisDB REST API security scan context</desc>
<inscope>true</inscope>
<incregexes>https://localhost:8443/v1/.*</incregexes>
<tech>
<include>Db.CouchDB</include>
</tech>
<authentication>
<type>2</type>
<loginurl>https://localhost:8443/v1/auth/login</loginurl>
<loginbody>{"username":"test-admin","password":"test-password"}</loginbody>
</authentication>
<forcedUser>
<username>test-admin</username>
<credentials>
<credential><name>username</name><value>test-admin</value></credential>
<credential><name>password</name><value>test-password</value></credential>
</credentials>
</forcedUser>
</context>
</configuration>
ZAPXML
fi
# ── Run ZAP baseline scan ─────────────────────────────────────────────
- name: Run OWASP ZAP Baseline Scan
uses: zaproxy/action-baseline@v0.12.0
with:
target: 'https://localhost:8443'
docker_name: 'ghcr.io/zaproxy/zaproxy:stable'
rules_file_name: '.github/zap-rules.tsv'
cmd_options: >
-config scanner.attackStrength=MEDIUM
-config api.disablekey=true
-z "-config replacer.full_list(0).description=auth-header
-config replacer.full_list(0).enabled=true
-config replacer.full_list(0).matchtype=REQ_HEADER
-config replacer.full_list(0).matchstr=Authorization
-config replacer.full_list(0).replacement=Bearer\\ test-token"
fail_action: true
allow_issue_writing: true
# ── Upload ZAP report ─────────────────────────────────────────────────
- name: Upload ZAP Report
if: always()
uses: actions/upload-artifact@v4
with:
name: zap-dast-report-${{ github.run_id }}
path: |
report_html.html
report_json.json
retention-days: 90
- name: Stop ThemisDB container
if: always()
run: docker compose -f docker-compose.yml down --volumes