Skip to content

chore: tune ZAP DAST scan -- auth, timeouts, rules, report artifacts#1097

Merged
Aureliolo merged 2 commits intomainfrom
chore/dast-tuning
Apr 6, 2026
Merged

chore: tune ZAP DAST scan -- auth, timeouts, rules, report artifacts#1097
Aureliolo merged 2 commits intomainfrom
chore/dast-tuning

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

Summary

Tunes the weekly ZAP DAST scan for better coverage, performance, and signal-to-noise ratio.

Changes

  • JWT authentication: Generate a system-user JWT (same identity as Go CLI) before scanning so ZAP can hit authenticated endpoints. Previously every protected route returned 401/403.
  • Timeout controls: -T 20 (startup/passive cap), maxRuleDurationInMins=5, maxScanDurationInMins=30 prevent runaway scans.
  • Performance: threadPerHost=5 (up from 2) for faster localhost scanning.
  • Report artifacts: -J report.json -r report.html for machine-parseable and human-readable outputs.
  • Critical rules promoted to FAIL: SQL injection (40018), OS command injection (90020), SSRF (40046), XXE (90023) -- prepares for eventual fail_action: true.
  • Triaged noise suppressed: Changed 10049 from WARN to IGNORE, added 10104 (User Agent Fuzzer) as IGNORE. Once all alerts are IGNORE'd, the ZAP action auto-closes ZAP API Scan Report #760.

Test plan

  • CI-only changes (workflow + rules TSV) -- no application code affected
  • Next weekly DAST run (or manual workflow_dispatch) validates the config
  • Can verify JWT generation by triggering the workflow manually after merge

Review

Quick mode (CI config only, no substantive code changes).

Closes #1096

- Generate system-user JWT so ZAP scans authenticated endpoints
- Add timeout controls (-T 20, maxRuleDuration 5m, maxScanDuration 30m)
- Increase threadPerHost to 5 for faster localhost scanning
- Add JSON + HTML report artifact outputs
- Promote SQL injection, command injection, SSRF, XXE to FAIL
- IGNORE all triaged false positives (10049, 10104, 90005)
- Existing triaged rules kept as IGNORE

Closes #1096
Copilot AI review requested due to automatic review settings April 6, 2026 20:15
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 6, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0c07acc9-1dc2-4a1f-b963-9215c53bfca7

📥 Commits

Reviewing files that changed from the base of the PR and between 1b6a1e5 and c43fde1.

⛔ Files ignored due to path filters (1)
  • .github/zap-rules.tsv is excluded by !**/*.tsv
📒 Files selected for processing (1)
  • .github/workflows/dast.yml
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (1)
.github/workflows/**/*.yml

📄 CodeRabbit inference engine (CLAUDE.md)

Use dorny/paths-filter for GitHub Actions path filtering. Jobs only run when their domain is affected. CLI has its own workflow (cli.yml).

Files:

  • .github/workflows/dast.yml
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to .github/zap-rules.tsv : ZAP DAST security scanning rules in `.github/zap-rules.tsv`
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Security: SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies: disabled/weighted/per-category/milestone), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/docker.yml : Docker workflow: builds backend + web + sandbox images, pushes to GHCR, signs with cosign. SLSA L3 provenance attestations via actions/attest-build-provenance. Scans: Trivy (CRITICAL = hard fail, HIGH = warn) + Grype (critical cutoff) + CIS Docker Benchmark v1.6.0 compliance (informational). CVE triage via .github/.trivyignore.yaml and .github/.grype.yaml. Images only pushed after scans pass. Triggers on push to main and version tags (v*).
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/docker.yml : Docker workflow: builds backend + web + sandbox images, pushes to GHCR, signs with cosign. SLSA L3 provenance attestations via actions/attest-build-provenance. Scans: Trivy (CRITICAL = hard fail, HIGH = warn) + Grype (critical cutoff) + CIS Docker Benchmark v1.6.0 compliance (informational). CVE triage via .github/.trivyignore.yaml and .github/.grype.yaml. Images only pushed after scans pass. Triggers on push to main and version tags (v*).

Applied to files:

  • .github/workflows/dast.yml
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to .github/workflows/**/*.yml : Path filtering: dorny/paths-filter detects Python/dashboard/docker changes; jobs only run when their domain is affected. CLI has its own workflow (cli.yml).

Applied to files:

  • .github/workflows/dast.yml
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: Docker workflow (`docker.yml`): build + Trivy/Grype scan + push to GHCR + cosign sign + SLSA L3 provenance. CVE triage: `.github/.trivyignore.yaml`, `.github/.grype.yaml`

Applied to files:

  • .github/workflows/dast.yml
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to .github/zap-rules.tsv : ZAP DAST security scanning rules in `.github/zap-rules.tsv`

Applied to files:

  • .github/workflows/dast.yml
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to .github/workflows/pages.yml : Pages job: version extraction from `pyproject.toml`, OpenAPI export, Astro + Zensical docs build (with version banner), GitHub Pages deploy on push to main

Applied to files:

  • .github/workflows/dast.yml
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/*.yml : Dependabot: daily updates for uv + github-actions + npm + pre-commit + docker + gomod, grouped minor/patch, no auto-merge. Use `/review-dep-pr` to review Dependabot PRs before merging.

Applied to files:

  • .github/workflows/dast.yml
📚 Learning: 2026-03-15T12:00:18.113Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T12:00:18.113Z
Learning: Applies to .github/workflows/docker.yml : CI Docker: build → scan → push to GHCR + cosign sign + SLSA L3 provenance via attest-build-provenance (images only pushed after Trivy/Grype scans pass).

Applied to files:

  • .github/workflows/dast.yml
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/api/**/*.py : Authentication uses JWT + API key. Approval gate integration for high-risk operations.

Applied to files:

  • .github/workflows/dast.yml
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to docker/{Dockerfile*,compose.yml} : Docker: Backend uses 3-stage build (builder → setup → distroless runtime), Chainguard Python, non-root (UID 65532), CIS-hardened. Web uses nginxinc/nginx-unprivileged, Vue 3 SPA with PrimeVue + Tailwind CSS, SPA routing, API/WebSocket proxy to backend.

Applied to files:

  • .github/workflows/dast.yml
🔇 Additional comments (3)
.github/workflows/dast.yml (3)

96-112: ZAP configuration and auth injection look appropriate.

The environment variables ZAP_AUTH_HEADER, ZAP_AUTH_HEADER_VALUE, and ZAP_AUTH_HEADER_SITE are the documented way to inject authentication headers in zaproxy/action-api-scan. Scoping to localhost ensures the token isn't accidentally sent to external URLs if ZAP follows redirects.

The timeout and threading configuration (-T 20, maxRuleDurationInMins=5, maxScanDurationInMins=30, threadPerHost=5) provides reasonable guardrails for CI scan duration.


114-122: Artifact upload properly preserves reports regardless of scan outcome.

The if: always() ensures reports are uploaded even when the scan step fails, which is essential for triaging findings. The 30-day retention aligns with typical security review cycles.


57-85: JWT generation is correct and system user bootstrap is properly implemented.

The token payload correctly includes all required claims (jti, sub, iss, aud, iat, exp), with values matching the constants defined in src/synthorg/api/auth/system_user.py (SYSTEM_USER_ID="system", SYSTEM_ISSUER="synthorg-cli", SYSTEM_AUDIENCE="synthorg-backend"). The masking of the token is good security hygiene. The system user is bootstrapped during backend startup via ensure_system_user() called in src/synthorg/api/lifecycle.py:207, ensuring the database lookup in _resolve_jwt_user() succeeds.


Walkthrough

The workflow now generates a JWT inside the running backend container via a Python snippet, masks it in logs, and exposes it as a step output. The ZAP scan action is configured to send this token as an Authorization: Bearer <token> header scoped to localhost. ZAP command options expand from -a to -a -T 20 and include -J report.json and -r report.html. Scanner performance and timeouts are set via environment variables (scanner.threadPerHost, scanner.maxRuleDurationInMins, scanner.maxScanDurationInMins). Report artifacts are uploaded after the scan.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: tuning ZAP DAST scan configuration for authentication, timeouts, and report artifacts.
Description check ✅ Passed The description clearly outlines all key changes including JWT auth, timeout controls, performance tuning, report artifacts, and rule adjustments, all directly related to the workflow modifications.
Linked Issues check ✅ Passed The PR fully addresses objectives from issues #760 and #1096: suppresses triaged alerts (10049, 10104, 90005), adds JWT authentication for protected endpoints, implements timeout controls and performance tuning, generates report artifacts, and promotes critical rules to FAIL.
Out of Scope Changes check ✅ Passed All changes are scoped to the DAST workflow configuration and rules file; no unrelated modifications outside the stated objectives are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the ZAP DAST rules configuration by categorizing critical vulnerabilities that should trigger a scan failure and triaging several informational rules. The review feedback suggests expanding the critical failure list to include other high-impact vulnerabilities like Cross-Site Scripting and Path Traversal to ensure more comprehensive security coverage.

Comment on lines +6 to +9
40018 FAIL SQL Injection
90020 FAIL Remote OS Command Injection
40046 FAIL Server Side Request Forgery
90023 FAIL XML External Entity (XXE)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The list of critical rules to trigger a failure is missing several high-impact vulnerabilities typically included in DAST security gates, such as Cross-Site Scripting (XSS) and Path Traversal. Consider adding these to ensure comprehensive coverage when fail_action is enabled.

Suggested additions:

  • 40012: Cross Site Scripting (Reflected)
  • 40014: Cross Site Scripting (Persistent)
  • 6: Path Traversal
  • 90024: Generic Remote Code Execution
40018	FAIL	SQL Injection
90020	FAIL	Remote OS Command Injection
40046	FAIL	Server Side Request Forgery
90023	FAIL	XML External Entity (XXE)
40012	FAIL	Cross Site Scripting (Reflected)
40014	FAIL	Cross Site Scripting (Persistent)
6	FAIL	Path Traversal
90024	FAIL	Generic Remote Code Execution

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 6, 2026

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA c43fde1.
Ensure that dependencies are being submitted on PR branches. Re-running this action after a short time may resolve the issue. See the documentation for more information and troubleshooting advice.

OpenSSF Scorecard

PackageVersionScoreDetails
actions/actions/upload-artifact bbbca2ddaa5d8feaa63e36b76fdaad77386f024f 🟢 5.7
Details
CheckScoreReason
Code-Review🟢 10all changesets reviewed
Maintained🟢 54 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 5
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Binary-Artifacts🟢 10no binaries found in the repo
Packaging⚠️ -1packaging workflow not detected
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Pinned-Dependencies⚠️ 1dependency not pinned by hash detected -- score normalized to 1
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Security-Policy🟢 9security policy file detected
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST🟢 10SAST tool is run on all commits

Scanned Files

  • .github/workflows/dast.yml

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the weekly ZAP DAST GitHub Actions workflow configuration to improve scan coverage (authenticated endpoints), cap scan runtimes, reduce alert noise via rule tuning, and generate richer scan outputs.

Changes:

  • Add workflow step to generate a system-user JWT and pass it to ZAP for authenticated scanning.
  • Add ZAP timeout/performance options and enable JSON/HTML report outputs.
  • Update ZAP rules TSV to promote critical findings to FAIL and suppress additional known-noise alerts.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
.github/zap-rules.tsv Updates ZAP alert action levels (FAIL/IGNORE) and reorganizes rules into “critical” vs “noise” sections.
.github/workflows/dast.yml Generates a JWT for authenticated scanning; adds scan timeouts/perf tuning and report output options.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +69 to +81
python -c "
import os, jwt, datetime
secret = os.environ['SYNTHORG_JWT_SECRET']
payload = {
'sub': 'system',
'iss': 'synthorg-cli',
'aud': 'synthorg-backend',
'role': 'system',
'iat': datetime.datetime.now(datetime.timezone.utc),
'exp': datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(hours=2),
}
print(jwt.encode(payload, secret, algorithm='HS256'))
")
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The embedded python -c script is indented inside the quoted string (leading spaces before import ...). Python treats leading indentation at top level as an IndentationError, so this step will fail and no token will be produced. Consider using a heredoc (python - <<'PY' ... PY) or otherwise ensure the script has no leading indentation.

Suggested change
python -c "
import os, jwt, datetime
secret = os.environ['SYNTHORG_JWT_SECRET']
payload = {
'sub': 'system',
'iss': 'synthorg-cli',
'aud': 'synthorg-backend',
'role': 'system',
'iat': datetime.datetime.now(datetime.timezone.utc),
'exp': datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(hours=2),
}
print(jwt.encode(payload, secret, algorithm='HS256'))
")
python - <<'PY'
import os, jwt, datetime
secret = os.environ['SYNTHORG_JWT_SECRET']
payload = {
'sub': 'system',
'iss': 'synthorg-cli',
'aud': 'synthorg-backend',
'role': 'system',
'iat': datetime.datetime.now(datetime.timezone.utc),
'exp': datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(hours=2),
}
print(jwt.encode(payload, secret, algorithm='HS256'))
PY
)

Copilot uses AI. Check for mistakes.
Comment on lines +72 to +80
payload = {
'sub': 'system',
'iss': 'synthorg-cli',
'aud': 'synthorg-backend',
'role': 'system',
'iat': datetime.datetime.now(datetime.timezone.utc),
'exp': datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(hours=2),
}
print(jwt.encode(payload, secret, algorithm='HS256'))
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generated JWT payload is missing the jti claim. AuthService.decode_token() requires exp, iat, sub, and jti, so the backend will reject this token as invalid and ZAP still won't be able to access authenticated endpoints. Include a unique jti (e.g., UUID hex) when generating the token.

Copilot uses AI. Check for mistakes.
@@ -1,8 +1,17 @@
# ZAP DAST rules -- see docs/security.md "DAST Tuning" for rationale.
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This rules file references docs/security.md "DAST Tuning" for rationale, but that section currently documents different actions/descriptions for some of these rules (e.g., 10049 and the 90005 wording). Updating the docs alongside the rules helps avoid drift and confusion during future triage.

Suggested change
# ZAP DAST rules -- see docs/security.md "DAST Tuning" for rationale.
# ZAP DAST rules -- this file is the source of truth for current rule actions and rationale.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/dast.yml:
- Around line 57-65: Update the "Generate DAST JWT" comment to explicitly
reference where the system-user bypass is implemented in the auth middleware so
future maintainers can trace it; mention the specific symbols that implement the
bypass (e.g., the pwd_sig check and the auth middleware function that skips it,
such as validatePasswordSignature or AuthMiddleware/Authenticate) so readers
know to inspect those functions when auth behavior changes, and optionally add a
short note pointing to the validation branch that skips pwd_sig for the "system"
subject.
- Around line 104-109: The workflow generates ZAP reports via the cmd_options
flags "-J report.json -r report.html" but never uploads them; add an
upload-artifact step immediately after the ZAP scan (and before the "Stop
backend" step) that uses actions/upload-artifact@v4 with if: always(), name:
zap-reports, path listing report.json and report.html, and retention-days: 30 so
the generated report.json and report.html are preserved as artifacts.
- Around line 68-81: The generated JWT payload is missing the required "jti"
claim so PyJWT validation (options={"require": ["exp","iat","sub","jti"]})
fails; update the inline Python block that builds payload (the payload dict used
before jwt.encode) to include a unique "jti" value (e.g., from uuid.uuid4()) so
the token printed by jwt.encode includes jti; ensure the change is made in the
same inline script invoked by docker compose exec that reads SYNTHORG_JWT_SECRET
and prints the token.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4e0b7376-2b3d-4806-94b4-1c3b4a21c70f

📥 Commits

Reviewing files that changed from the base of the PR and between 35977c0 and 1b6a1e5.

⛔ Files ignored due to path filters (1)
  • .github/zap-rules.tsv is excluded by !**/*.tsv
📒 Files selected for processing (1)
  • .github/workflows/dast.yml
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Agent
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (1)
.github/workflows/**/*.yml

📄 CodeRabbit inference engine (CLAUDE.md)

Use dorny/paths-filter for GitHub Actions path filtering. Jobs only run when their domain is affected. CLI has its own workflow (cli.yml).

Files:

  • .github/workflows/dast.yml
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to .github/zap-rules.tsv : ZAP DAST security scanning rules in `.github/zap-rules.tsv`
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Security: SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies: disabled/weighted/per-category/milestone), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume).
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to .github/zap-rules.tsv : ZAP DAST security scanning rules in `.github/zap-rules.tsv`

Applied to files:

  • .github/workflows/dast.yml
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/docker.yml : Docker workflow: builds backend + web + sandbox images, pushes to GHCR, signs with cosign. SLSA L3 provenance attestations via actions/attest-build-provenance. Scans: Trivy (CRITICAL = hard fail, HIGH = warn) + Grype (critical cutoff) + CIS Docker Benchmark v1.6.0 compliance (informational). CVE triage via .github/.trivyignore.yaml and .github/.grype.yaml. Images only pushed after scans pass. Triggers on push to main and version tags (v*).

Applied to files:

  • .github/workflows/dast.yml
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: Docker workflow (`docker.yml`): build + Trivy/Grype scan + push to GHCR + cosign sign + SLSA L3 provenance. CVE triage: `.github/.trivyignore.yaml`, `.github/.grype.yaml`

Applied to files:

  • .github/workflows/dast.yml
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to docker/{Dockerfile*,compose.yml} : Docker: Backend uses 3-stage build (builder → setup → distroless runtime), Chainguard Python, non-root (UID 65532), CIS-hardened. Web uses nginxinc/nginx-unprivileged, Vue 3 SPA with PrimeVue + Tailwind CSS, SPA routing, API/WebSocket proxy to backend.

Applied to files:

  • .github/workflows/dast.yml
🔇 Additional comments (1)
.github/workflows/dast.yml (1)

96-99: LGTM!

The ZAP authentication environment variables are correctly configured. Restricting ZAP_AUTH_HEADER_SITE to localhost ensures the Bearer token is only sent to the local backend and not leaked to any external URLs that ZAP might encounter.

Comment on lines +57 to +65
# Generate a long-lived system-user JWT so ZAP can scan authenticated
# endpoints. Without this, every protected route returns 401/403 and
# is effectively invisible to the scanner.
#
# Uses the same system-user identity (sub=system, iss=synthorg-cli,
# aud=synthorg-backend) that the Go CLI uses. The system user is
# bootstrapped at backend startup and skips pwd_sig validation --
# the JWT HMAC signature is the sole auth gate.
- name: Generate DAST JWT
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider documenting the system-user token bypass behavior.

The comment explains this uses the same identity as the Go CLI and mentions "skips pwd_sig validation," but it would help future maintainers to reference where this bypass is implemented in the auth middleware. This aids traceability when auth behavior changes.

📝 Suggested comment enhancement
       # Generate a long-lived system-user JWT so ZAP can scan authenticated
       # endpoints.  Without this, every protected route returns 401/403 and
       # is effectively invisible to the scanner.
       #
       # Uses the same system-user identity (sub=system, iss=synthorg-cli,
       # aud=synthorg-backend) that the Go CLI uses.  The system user is
       # bootstrapped at backend startup and skips pwd_sig validation --
       # the JWT HMAC signature is the sole auth gate.
+      # See: src/synthorg/api/auth/middleware.py _resolve_jwt_user()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Generate a long-lived system-user JWT so ZAP can scan authenticated
# endpoints. Without this, every protected route returns 401/403 and
# is effectively invisible to the scanner.
#
# Uses the same system-user identity (sub=system, iss=synthorg-cli,
# aud=synthorg-backend) that the Go CLI uses. The system user is
# bootstrapped at backend startup and skips pwd_sig validation --
# the JWT HMAC signature is the sole auth gate.
- name: Generate DAST JWT
# Generate a long-lived system-user JWT so ZAP can scan authenticated
# endpoints. Without this, every protected route returns 401/403 and
# is effectively invisible to the scanner.
#
# Uses the same system-user identity (sub=system, iss=synthorg-cli,
# aud=synthorg-backend) that the Go CLI uses. The system user is
# bootstrapped at backend startup and skips pwd_sig validation --
# the JWT HMAC signature is the sole auth gate.
# See: src/synthorg/api/auth/middleware.py _resolve_jwt_user()
- name: Generate DAST JWT
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/dast.yml around lines 57 - 65, Update the "Generate DAST
JWT" comment to explicitly reference where the system-user bypass is implemented
in the auth middleware so future maintainers can trace it; mention the specific
symbols that implement the bypass (e.g., the pwd_sig check and the auth
middleware function that skips it, such as validatePasswordSignature or
AuthMiddleware/Authenticate) so readers know to inspect those functions when
auth behavior changes, and optionally add a short note pointing to the
validation branch that skips pwd_sig for the "system" subject.

…facts

- Add missing jti claim to JWT payload (required by decode_token)
- Add XSS (40012, 40014), Path Traversal (6), RCE (90024) to FAIL rules
- Upload report.json and report.html as workflow artifacts
- Add _resolve_jwt_user() cross-reference in JWT generation comment
- Fix stale docs/security.md reference in rules file header
@Aureliolo Aureliolo merged commit 82bf0e1 into main Apr 6, 2026
25 checks passed
@Aureliolo Aureliolo deleted the chore/dast-tuning branch April 6, 2026 20:26
Aureliolo added a commit that referenced this pull request Apr 6, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.6.3](v0.6.2...v0.6.3)
(2026-04-06)


### Features

* backend CRUD + multi-user permissions
([#1081](#1081),
[#1082](#1082))
([#1094](#1094))
([93e469b](93e469b))
* in-dashboard team editing + budget rebalance on pack apply
([#1093](#1093))
([35977c0](35977c0)),
closes [#1079](#1079)
[#1080](#1080)
* tiered rate limiting, NotificationSink protocol, in-dashboard
notifications
([#1092](#1092))
([df2142c](df2142c)),
closes [#1077](#1077)
[#1078](#1078)
[#849](#849)
* two-stage safety classifier and cross-provider uncertainty check for
approval gates
([#1090](#1090))
([0b2edee](0b2edee)),
closes [#847](#847)
[#701](#701)


### Refactoring

* memory pipeline improvements
([#1075](#1075),
[#997](#997))
([#1091](#1091))
([a048a4c](a048a4c))


### Documentation

* add OpenCode parity setup and hookify rule documentation
([#1095](#1095))
([52e877a](52e877a))


### Maintenance

* bump vite from 8.0.3 to 8.0.4 in /web in the all group across 1
directory ([#1088](#1088))
([1e86ca6](1e86ca6))
* tune ZAP DAST scan -- auth, timeouts, rules, report artifacts
([#1097](#1097))
([82bf0e1](82bf0e1)),
closes [#1096](#1096)

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore: tune ZAP DAST scan -- auth, timeouts, rules, report artifacts ZAP API Scan Report

2 participants