chore: tune ZAP DAST scan -- auth, timeouts, rules, report artifacts#1097
chore: tune ZAP DAST scan -- auth, timeouts, rules, report artifacts#1097
Conversation
- 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
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
📜 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)
🧰 Additional context used📓 Path-based instructions (1).github/workflows/**/*.yml📄 CodeRabbit inference engine (CLAUDE.md)
Files:
🧠 Learnings (10)📓 Common learnings📚 Learning: 2026-03-15T21:32:02.880ZApplied to files:
📚 Learning: 2026-03-15T18:17:43.675ZApplied to files:
📚 Learning: 2026-03-21T14:12:17.848ZApplied to files:
📚 Learning: 2026-03-31T14:17:24.182ZApplied to files:
📚 Learning: 2026-04-02T12:21:16.739ZApplied to files:
📚 Learning: 2026-03-15T21:32:02.880ZApplied to files:
📚 Learning: 2026-03-15T12:00:18.113ZApplied to files:
📚 Learning: 2026-03-15T19:14:27.144ZApplied to files:
📚 Learning: 2026-03-15T18:17:43.675ZApplied to files:
🔇 Additional comments (3)
WalkthroughThe workflow now generates a JWT inside the running 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
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.
| 40018 FAIL SQL Injection | ||
| 90020 FAIL Remote OS Command Injection | ||
| 40046 FAIL Server Side Request Forgery | ||
| 90023 FAIL XML External Entity (XXE) |
There was a problem hiding this comment.
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
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Snapshot WarningsEnsure 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
Scanned Files
|
There was a problem hiding this comment.
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
FAILand 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.
| 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')) | ||
| ") |
There was a problem hiding this comment.
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.
| 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 | |
| ) |
| 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')) |
There was a problem hiding this comment.
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.
.github/zap-rules.tsv
Outdated
| @@ -1,8 +1,17 @@ | |||
| # ZAP DAST rules -- see docs/security.md "DAST Tuning" for rationale. | |||
There was a problem hiding this comment.
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.
| # 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. |
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (1)
.github/zap-rules.tsvis 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-filterfor 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_SITEtolocalhostensures the Bearer token is only sent to the local backend and not leaked to any external URLs that ZAP might encounter.
| # 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 |
There was a problem hiding this comment.
🧹 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.
| # 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
🤖 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).
Summary
Tunes the weekly ZAP DAST scan for better coverage, performance, and signal-to-noise ratio.
Changes
-T 20(startup/passive cap),maxRuleDurationInMins=5,maxScanDurationInMins=30prevent runaway scans.threadPerHost=5(up from 2) for faster localhost scanning.-J report.json -r report.htmlfor machine-parseable and human-readable outputs.fail_action: true.Test plan
workflow_dispatch) validates the configReview
Quick mode (CI config only, no substantive code changes).
Closes #1096