feat(security): add OWASP ZAP automated security scan - #724
Conversation
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
|
@muizbolaji4 is attempting to deploy a commit to the ritik4ever's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
@muizbolaji4 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
📝 WalkthroughWalkthroughChangesOWASP ZAP security scanning
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related issues
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
🧹 Nitpick comments (1)
.github/workflows/zap-security-scan.yml (1)
26-27: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winDisable persisted checkout credentials.
This workflow does not need authenticated Git operations after checkout. Set
persist-credentials: false; checkout v4 persists the token by default. (github.com)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/zap-security-scan.yml around lines 26 - 27, Update the “Checkout repository” step using actions/checkout@v4 to set persist-credentials to false, while leaving the existing checkout behavior unchanged.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/zap-security-scan.yml:
- Around line 68-85: Update the ZAP report handling around the zap-report.json
existence check and jq count calculations to fail closed: require a non-empty
report, validate its JSON/schema before counting alerts, and treat every jq or
summary-publication failure as an error. Ensure malformed or missing reports and
failed writes to GITHUB_STEP_SUMMARY exit nonzero instead of reporting a
successful scan, while preserving the existing blocking behavior for high- or
medium-severity findings.
- Around line 32-37: Update the target-selection step to pass each external URL
through the step environment rather than interpolating it into the shell script,
then emit the selected value with quoted printf into GITHUB_OUTPUT. Also update
the summary output to consume the step output safely without directly embedding
unvalidated steps.target.outputs.url in shell source; validate or escape it
before use.
- Around line 12-16: The workflow permissions must support SARIF uploads without
enabling unintended issue creation. Update the top-level permissions block to
grant security-events: write instead of checks: write, and configure
zaproxy/action-baseline@v0.10.0 with allow_issue_writing: false while preserving
the existing scan and upload steps.
- Around line 21-23: Change the deployment and ZAP scan workflow coordination so
the scan completes before deployment reports success, rather than gating on
github.event.deployment_status.state == 'success' in the current workflow
condition. Implement the required pre-success protection gate or make the
deployment workflow wait for the scan result, ensuring failed scans prevent
deployment success.
- Around line 21-23: Update the workflow trigger condition to require a staging
deployment environment whenever github.event_name is deployment_status, while
preserving the existing workflow_dispatch path. Use the deployment status
environment field exposed by the event payload and ensure successful production
or other non-staging deployments cannot start the ZAP scan.
- Around line 43-49: Update the ZAP Baseline Passive Scan step identified by id
zap to set fail_action to false, allowing the scan to complete without failing
on its default alert handling. Preserve the later explicit report check as the
workflow-level gate based on parsed medium and high risk counts.
- Around line 43-49: The ZAP workflow references report filenames that the
baseline action does not generate. Update the JSON and HTML processing steps to
use report_json.json and report_html.html, and update Markdown handling to use
report_md.md; if SARIF remains required, add a separate conversion step that
produces the SARIF file before upload.
- Around line 34-35: Update the URL assignment in the workflow’s
deployment-status handling to use github.event.deployment_status.environment_url
instead of github.event.deployment_status.target_url. Preserve the existing
non-empty check and GITHUB_OUTPUT assignment so ZAP receives the deployed
environment URL.
- Line 45: Update the zaproxy/action-baseline reference in the workflow to the
latest supported release that declares runs.using node20, and verify that its
inputs and outputs remain compatible with the existing scan step before merging.
---
Nitpick comments:
In @.github/workflows/zap-security-scan.yml:
- Around line 26-27: Update the “Checkout repository” step using
actions/checkout@v4 to set persist-credentials to false, while leaving the
existing checkout behavior unchanged.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: fc62ccdf-3fca-4e43-a438-9720bb8243c4
📒 Files selected for processing (1)
.github/workflows/zap-security-scan.yml
| permissions: | ||
| contents: read | ||
| deployments: read | ||
| checks: write | ||
|
|
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== workflow =="
if [ -f .github/workflows/zap-security-scan.yml ]; then
nl -ba .github/workflows/zap-security-scan.yml | sed -n '1,120p'
else
echo "missing .github/workflows/zap-security-scan.yml"
fi
echo
echo "== search for action calls =="
rg -n "github/codeql-action/upload-sarif|zaproxy/action-baseline|permissions:|allow_issue_writing" .github/workflows || true
echo
echo "== git diff summary =="
git diff --stat || trueRepository: ritik4ever/stellar-goal-vault
Length of output: 222
🌐 Web query:
github codeql-action upload-sarif permissions security-events write
💡 Result:
To use the github/codeql-action/upload-sarif action, your GitHub Actions workflow job must be granted specific permissions to interact with the repository's security features and metadata [1][2]. The mandatory permission is: - security-events: write In addition to this, if you are working in a private or internal repository, you must also grant: - actions: read - contents: read These additional permissions are required because the action queries workflow run metadata during the upload process [3][4]. Failing to provide these permissions—particularly actions: read in private repositories—often results in a "Resource not accessible by integration" error [5][3]. Example configuration: jobs: upload-analysis: runs-on: ubuntu-latest permissions: security-events: write actions: read contents: read steps: - name: Upload SARIF uses: github/codeql-action/upload-sarif@v4 with: sarif_file: results.sarif
Citations:
- 1: https://docs.github.com/en/code-security/how-tos/find-and-fix-code-vulnerabilities/integrate-with-existing-tools/upload-sarif-file
- 2: https://github.com/github/codeql-action/blob/2a6fe1608c7d5b1e0cb3d8e19d51c4ff1ca47a0d/README.md
- 3: codeql/upload-sarif@v3 action failed: Resource not accessible by integration - missing
actions: readgithub/codeql-action#2117 - 4: Add
actions: readto generated SARIF upload job permissions for private repos github/gh-aw#37367 - 5: codeql/upload-sarif@v2 or @main can't integrate on actions github/codeql-action#1806
🌐 Web query:
zaproxy action-baseline v0.10.0 README allow_issue_writing default true
💡 Result:
In the zaproxy/action-baseline GitHub Action, specifically in version 0.10.0 and subsequent versions, the allow_issue_writing input defaults to true [1][2]. This input determines whether the action will automatically create or update a GitHub issue with the ZAP scan report using the title provided in the issue_title input [3][1]. Setting allow_issue_writing to false disables this behavior [3][4].
Citations:
- 1: zaproxy/action-baseline@49c3b71
- 2: https://github.com/zaproxy/action-baseline/blob/master/action.yml
- 3: https://github.com/zaproxy/action-baseline/blob/master/README.md
- 4: https://github.com/marketplace/actions/zap-baseline-scan
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== workflow =="
if [ -f .github/workflows/zap-security-scan.yml ]; then
awk '{ printf "%5d %s\n", NR, $0 }' .github/workflows/zap-security-scan.yml | sed -n '1,120p'
else
echo "missing .github/workflows/zap-security-scan.yml"
fi
echo
echo "== search for action calls and permissions =="
rg -n "github/codeql-action/upload-sarif|zaproxy/action-baseline|permissions:|allow_issue_writing|permissions:" .github/workflows || true
echo
echo "== git status/diff summary =="
git status --short || true
git diff --stat || trueRepository: ritik4ever/stellar-goal-vault
Length of output: 4595
Grant the permissions required by the SARIF upload action.
github/codeql-action/upload-sarif@v3 requires security-events: write; checks: write does not grant that permission, so the final SARIF upload step can fail with Resource not accessible by integration. Also set allow_issue_writing: false on zaproxy/action-baseline@v0.10.0 unless creating/updating ZAP issues is intentional; issue writing is enabled by default and can fail when issue permissions are not granted.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/zap-security-scan.yml around lines 12 - 16, The workflow
permissions must support SARIF uploads without enabling unintended issue
creation. Update the top-level permissions block to grant security-events: write
instead of checks: write, and configure zaproxy/action-baseline@v0.10.0 with
allow_issue_writing: false while preserving the existing scan and upload steps.
| if: > | ||
| github.event_name == 'workflow_dispatch' || | ||
| github.event.deployment_status.state == 'success' |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files '.github/workflows/*' | sed -n '1,120p'
echo
echo "== target workflow =="
if [ -f .github/workflows/zap-security-scan.yml ]; then
cat -n .github/workflows/zap-security-scan.yml
else
echo "missing .github/workflows/zap-security-scan.yml"
fi
echo
echo "== related workflow references =="
rg -n "zap-security-scan|workflow_dispatch|deployment_status|deployment|deployment_status\.state|ref =|ZAP|zap" .github/workflows || true
echo
echo "== current branch/workflow files diff stat =="
git diff --stat -- .github/workflows || trueRepository: ritik4ever/stellar-goal-vault
Length of output: 7549
🌐 Web query:
GitHub Actions deployment_status event runs after status reports back deployment workflow cannot be blocked by downstream job after deployment_status success
💡 Result:
In GitHub Actions, a workflow configured with the deployment_status event trigger runs independently whenever an external service updates the deployment status (e.g., to success, failure, or pending) [1][2][3]. This workflow is not "blocked" or managed by the workflow that originally created the deployment; rather, it is a separate event-driven process [4][3]. Because the deployment_status event fires for every state change, your workflow will be triggered multiple times throughout the deployment lifecycle [4]. If you only want your workflow jobs to execute after a successful deployment, you must explicitly filter them using a conditional expression at the job level [4][5]: jobs: run-after-deploy: if: github.event.deployment_status.state == 'success' runs-on: ubuntu-latest steps: - run: echo "Deployment was successful!" Regarding your query about downstream blocking: 1. Independent Execution: The deployment_status workflow is triggered by the GitHub webhook event, not by the completion of another workflow [1][3]. There is no native mechanism for a "downstream job" to block the initiation of this event-driven workflow [4]. 2. State Dependency: Since you cannot prevent the workflow from triggering on non-success states (like pending or in_progress) without potentially missing the success event, the standard pattern is to allow the workflow to start and then use the if condition to skip unwanted jobs [4][6]. 3. Context Limitations: Be aware that the deployment_status event context often lacks certain information, such as the github.ref (branch or tag), because the event is tied to a specific deployment instance rather than a repository ref [7][8]. If you need to verify the branch, you may need to use the GitHub API to fetch the associated commit or deployment details [8]. In summary, you cannot block the deployment_status event from triggering, nor can you block the overall workflow run from starting via a downstream job. You should design your workflow to be idempotent and use job-level conditionals to ensure your logic only proceeds when the deployment state reaches success [4][5].
Citations:
- 1: https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows
- 2: https://docs.github.com/en/rest/deployments/statuses
- 3: https://docs.github.com/en/rest/deployments/deployments
- 4: https://stackoverflow.com/questions/75988697/github-actions-pull-request-checks-on-deployment-status
- 5: https://github.com/railwayapp/docs/blob/76db91a4/content/guides/github-actions-post-deploy.md
- 6: https://stackoverflow.com/questions/69275058/cypress-test-runs-before-vercel-deployment-in-github-actions
- 7: [Feature Request] Expose ref for all deployment_status events actions/cache#598
- 8: https://stackoverflow.com/questions/69629468/github-workflow-only-on-deployment-status-and-specific-branch
Gate the scan before reporting deployment success.
This workflow is triggered by deployment_status, so it starts after the deployment status is already reported; if it fails after the state is success, the deployment cannot be rolled back by GitHub Actions alone. Satisfy Issue #628 by making this a required pre-success check/protection gate or having the deployment workflow wait for the scan result before reporting success.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/zap-security-scan.yml around lines 21 - 23, Change the
deployment and ZAP scan workflow coordination so the scan completes before
deployment reports success, rather than gating on
github.event.deployment_status.state == 'success' in the current workflow
condition. Implement the required pre-success protection gate or make the
deployment workflow wait for the scan result, ensuring failed scans prevent
deployment success.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Repository files matching zap-security-scan:"
fd -a 'zap-security-scan\.yml$|zap-security-scan\.yaml$' . || true
echo
echo "Workflow contents:"
if [ -f .github/workflows/zap-security-scan.yml ]; then
cat -n .github/workflows/zap-security-scan.yml
fi
echo
echo "Event/trigger references in workflows:"
rg -n "ghcr.io|zap|security|on:|deployment_status|workflow_dispatch|environment" .github/workflows || true
echo
echo "Relevant GitHub Actions docs via local source absent; checking public docs with web query"Repository: ritik4ever/stellar-goal-vault
Length of output: 11101
🌐 Web query:
GitHub Actions deployment_status object environment field event.deployment_status.environment
💡 Result:
In GitHub Actions workflows, the github.event.deployment_status.environment field contains the name of the target deployment environment (e.g., "production", "staging", "qa") associated with a deployment_status event [1][2][3]. Key details regarding this field include: * Data Source: The field is part of the deployment_status object within the webhook payload for the deployment_status event [4][2]. * Behavior: When a deployment status is created, this field identifies the specific environment being targeted [3][5]. If this field is not explicitly defined when the status is created, GitHub defaults to the environment of the previous status on the deployment, if one exists; otherwise, it defaults to the environment of the original deployment [6][3][7]. * Usage in Workflows: It is commonly used in GitHub Actions to filter jobs or trigger specific logic based on the environment being deployed to. For example, developers often use it in if conditions to ensure deployment-related workflows only execute for specific environments: on: deployment_status: jobs: deploy-check: runs-on: ubuntu-latest if: github.event.deployment_status.environment == 'production' steps: - run: echo "Deploying to production" * Security Warning: Because environment (along with other payload fields) is provided by the external service or tool that triggers the deployment, it should be treated as untrusted input [1]. When using this field to make security decisions or run tests, ensure your workflow is configured safely, as improper handling of deployment-related inputs can potentially be exploited [1].
Citations:
- 1: https://labs.boostsecurity.io/articles/deployment_poisoning/
- 2: https://github.com/octokit/webhooks/blob/6a8fec1a9e76780bba12b75291be104a1697d300/schema.d.ts
- 3: https://docs.github.com/rest/deployments/statuses
- 4: https://docs.github.com/en/webhooks/webhook-events-and-payloads
- 5: https://docs.github.com/enterprise-server@3.15/rest/deployments/statuses
- 6: https://docs.github.com/en/rest/deployments/statuses
- 7: https://docs.github.com/en/rest/deployments/statuses?apiVersion=2022-11-28
Restrict deployment-status scans to staging.
This workflow runs on every successful deployment-status event; gate that branch on the deployment environment so production or other environments cannot trigger the ZAP scan.
Proposed condition
github.event_name == 'workflow_dispatch' ||
- github.event.deployment_status.state == 'success'
+ (github.event.deployment_status.state == 'success' &&
+ github.event.deployment_status.environment == 'staging')📝 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.
| if: > | |
| github.event_name == 'workflow_dispatch' || | |
| github.event.deployment_status.state == 'success' | |
| if: > | |
| github.event_name == 'workflow_dispatch' || | |
| (github.event.deployment_status.state == 'success' && | |
| github.event.deployment_status.environment == 'staging') |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/zap-security-scan.yml around lines 21 - 23, Update the
workflow trigger condition to require a staging deployment environment whenever
github.event_name is deployment_status, while preserving the existing
workflow_dispatch path. Use the deployment status environment field exposed by
the event payload and ensure successful production or other non-staging
deployments cannot start the ZAP scan.
| if [ -n "${{ github.event.inputs.target_url }}" ]; then | ||
| echo "url=${{ github.event.inputs.target_url }}" >> $GITHUB_OUTPUT | ||
| elif [ -n "${{ github.event.deployment_status.target_url }}" ]; then | ||
| echo "url=${{ github.event.deployment_status.target_url }}" >> $GITHUB_OUTPUT | ||
| elif [ -n "${{ secrets.STAGING_URL }}" ]; then | ||
| echo "url=${{ secrets.STAGING_URL }}" >> $GITHUB_OUTPUT |
There was a problem hiding this comment.
🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== workflow file =="
cat -n .github/workflows/zap-security-scan.yml
echo
echo "== occurrences of github.event/secrets in workflow =="
rg -n '\$\{\{\s*(github\.event|secrets\.STAGING_URL)' .github/workflows/zap-security-scan.yml || trueRepository: ritik4ever/stellar-goal-vault
Length of output: 4642
Do not interpolate external values into shell source.
The target values on lines 32-37 are expanded directly into the run script, so inputs/secret values containing " and untrusted deployment URLs containing $() can alter the commands. Pass the target through env and use quoted printf for shell output; avoid interpolating steps.target.outputs.url into the summary line as well unless it is validated/escaped first.
🧰 Tools
🪛 zizmor (1.28.0)
[error] 32-32: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[error] 33-33: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[error] 34-34: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[error] 35-35: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/zap-security-scan.yml around lines 32 - 37, Update the
target-selection step to pass each external URL through the step environment
rather than interpolating it into the shell script, then emit the selected value
with quoted printf into GITHUB_OUTPUT. Also update the summary output to consume
the step output safely without directly embedding unvalidated
steps.target.outputs.url in shell source; validate or escape it before use.
Source: Linters/SAST tools
| elif [ -n "${{ github.event.deployment_status.target_url }}" ]; then | ||
| echo "url=${{ github.event.deployment_status.target_url }}" >> $GITHUB_OUTPUT |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== workflow occurrences =="
if [ -f .github/workflows/zap-security-scan.yml ]; then
nl -ba .github/workflows/zap-security-scan.yml | sed -n '1,140p'
else
echo "missing .github/workflows/zap-security-scan.yml"
fi
echo
echo "== deployment-status related references =="
rg -n "deployment_status|environment_url|target_url|url=|--url" .github/workflows || true
echo
echo "== docs/API behavior from installed GitHub REST docs? =="
python3 - <<'PY'
import json, urllib.request
url="https://docs.github.com/en/rest/deployments/statuses"
try:
with urllib.request.urlopen(url, timeout=20) as r:
text=r.read(500).decode()
print(text)
except Exception as e:
print(type(e).__name__, e)
PYRepository: ritik4ever/stellar-goal-vault
Length of output: 234
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== workflow occurrences =="
if [ -f .github/workflows/zap-security-scan.yml ]; then
awk '{print NR": "$0}' .github/workflows/zap-security-scan.yml | sed -n '1,140p'
else
echo "missing .github/workflows/zap-security-scan.yml"
fi
echo
echo "== deployment-status related references =="
rg -n "deployment_status|environment_url|target_url|url=|--url" .github/workflows || true
echo
echo "== external doc/API fields for deployment statuses =="
python3 - <<'PY'
from urllib.request import urlopen
fields=[]
for url in [
"https://docs.github.com/en/rest/deployments/deployments?apiVersion=2022-11-28",
"https://docs.github.com/en/rest/deployments/statuses?apiVersion=2022-11-28",
"https://rest.github-api.org/en/v1/rest/deployments/deployments?apiVersion=2022-11-28",
]:
try:
text=urlopen(url, timeout=20).read(5000).decode()
print(f"--- {url!r} ---")
print(text)
print()
except Exception as e:
print(f"--- {url!r} ---")
print(f"ERROR {type(e).__name__}: {e}")
print()
PYRepository: ritik4ever/stellar-goal-vault
Length of output: 5654
🌐 Web query:
GitHub REST Deployment Statuses fields target_url environment_url documentation
💡 Result:
In the GitHub REST API, both target_url and environment_url are fields used when creating or retrieving a deployment status [1][2]. 1. target_url: This is a string field intended to provide a link to the output of the deployment, which helps users monitor the task while it is running or access historical information about the deployment [1][3]. GitHub currently recommends using the log_url parameter instead of target_url [1][2]. While target_url is still accepted to support legacy integrations, log_url is the preferred field for providing the full URL of the deployment's output [1][4]. Notably, if you set log_url, GitHub automatically sets target_url to the same value [1][3]. 2. environment_url: This is a string field that specifies the URL for accessing the deployed environment (e.g., the live application URL) [1][2]. It is distinct from the target_url or log_url, which are typically used for deployment logs or task status tracking [1][3]. When interacting with the POST /repos/{owner}/{repo}/deployments/{deployment_id}/statuses endpoint, both target_url (or log_url) and environment_url are optional fields provided in the request body [1][4]. Both fields are also returned as part of the deployment status object when retrieving status information [1][2].
Citations:
- 1: https://docs.github.com/en/rest/deployments/statuses
- 2: https://docs.github.com/rest/deployments/statuses
- 3: https://docs.github.com/en/enterprise-cloud@latest/rest/deployments/statuses
- 4: https://docs.github.com/en/enterprise-server@3.21/rest/deployments/statuses?apiVersion=2026-03-10
Use environment_url as the scan target.
deployment_status.target_url is intended for deployment output/log links, not the deployed environment/app URL. Set url from github.event.deployment_status.environment_url so the ZAP scan does not target a deployment log.
🧰 Tools
🪛 zizmor (1.28.0)
[error] 34-34: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[error] 35-35: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/zap-security-scan.yml around lines 34 - 35, Update the URL
assignment in the workflow’s deployment-status handling to use
github.event.deployment_status.environment_url instead of
github.event.deployment_status.target_url. Preserve the existing non-empty check
and GITHUB_OUTPUT assignment so ZAP receives the deployed environment URL.
| - name: ZAP Baseline Passive Scan | ||
| id: zap | ||
| uses: zaproxy/action-baseline@v0.10.0 | ||
| with: | ||
| target: ${{ steps.target.outputs.url }} | ||
| cmd_options: '-I -T 60' | ||
| fail_action: true |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== workflow excerpt =="
sed -n '1,140p' .github/workflows/zap-security-scan.yml
echo
echo "== zap-related files =="
rg -n "riskcode|fail_action|baseline|severity|counts|ZAP|zap" .github/workflows/zap-security-scan.yml .github 2>/dev/null || true
echo
echo "== action usage references =="
rg -n "zaproxy/action-baseline|fail_action|cmd_options" .github/workflows || trueRepository: ritik4ever/stellar-goal-vault
Length of output: 9179
🌐 Web query:
ZAP action baseline fail_action true WARN FAIL riskcode documentation
💡 Result:
In the context of the OWASP ZAP Baseline Scan GitHub Action, the fail_action parameter is used to control whether the GitHub workflow run status is marked as failed when ZAP identifies security alerts [1][2][3]. When set to true, the GitHub Action will fail if the underlying ZAP baseline scan identifies any alerts or fails to scan the target [2]. If set to false (the default), the action will report the results but will not fail the GitHub workflow status due to identified alerts [2][3]. Regarding the control of alert levels (WARN, FAIL, etc.), this is managed separately from the fail_action parameter. You can configure which rules trigger a failure using a ZAP configuration file [4][5]. In this file, you can change the alert level of specific rules to FAIL, which will cause the ZAP scan script itself to return a non-zero exit code, or IGNORE, which suppresses them [4][6][7]. The baseline scan script typically classifies alerts as WARN by default [5]. When running the ZAP Docker container, you can pass a configuration file using the -c command-line option to customize these rule levels [4][7]. In summary: - fail_action: true (in the GitHub Action) determines if the overall CI/CD job fails when any alerts are found [2]. - Configuration file (rules.tsv): Used to define which specific alert risk codes or rules should be treated as FAIL (causing the scan to exit with a failure code) or IGNORE (ignoring them entirely) [4][5]. Top results: [1], [2], [4], [5]
Citations:
- 1: https://github.com/zaproxy/action-baseline
- 2: zaproxy/action-baseline@253d40b
- 3: https://github.com/zaproxy/action-baseline/blob/f40b7873885b5fad99a7b245db71a8547844f492/action.yml
- 4: https://www.zaproxy.org/docs/docker/baseline-scan/
- 5: https://www.zaproxy.org/blog/2020-04-09-automate-security-testing-with-zap-and-github-actions/
- 6: https://github.com/zaproxy/zaproxy/blob/0c2975a77e22b28803c910ca42c7e8e4d0df0d8b/docker/zap-baseline.py
- 7: https://deepwiki.com/zaproxy/zaproxy/3.2-scan-scripts
Make the parsed medium/high alert count the gate.
fail_action: true makes this step fail when ZAP Baseline finds alerts, but Baseline rules default to WARN, and -I prevents warning rules from making the scan exit non-zero unless they are reconfigured as FAIL. The later explicit check counts riskcode == "2" and "3", so if the two paths disagree the summary can say deploy is blocked after the job already failed for unrelated warnings. Set fail_action: false and use the explicit report check as the workflow-level failure.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/zap-security-scan.yml around lines 43 - 49, Update the ZAP
Baseline Passive Scan step identified by id zap to set fail_action to false,
allowing the scan to complete without failing on its default alert handling.
Preserve the later explicit report check as the workflow-level gate based on
parsed medium and high risk counts.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== workflow excerpt =="
if [ -f .github/workflows/zap-security-scan.yml ]; then
nl -ba .github/workflows/zap-security-scan.yml | sed -n '1,120p'
else
echo "workflow not found"
fi
echo
echo "== zap/action references =="
rg -n "zaproxy/action-baseline|action-baseline|report_json|report_html|report_md|results\.sarif|zap-report|sarif|sarif|artifacts|upload-artifact" .github/workflows || trueRepository: ritik4ever/stellar-goal-vault
Length of output: 230
🌐 Web query:
zaproxy action-baseline v0.10.0 report_json.json report_html.html report_md.md sarif
💡 Result:
The zaproxy/action-baseline GitHub Action, including version 0.10.0 [1][2], facilitates ZAP baseline scans by wrapping the ZAP Docker baseline scan script [3][4]. Regarding the requested report files (report_json.json, report_html.html, report_md.md) and SARIF support: 1. JSON, HTML, and Markdown Reports: The underlying ZAP baseline scan script supports generating these reports via command-line flags (-J, -r, -w) [5][6]. To use these with the GitHub Action, you must pass the corresponding flags through the cmd_options input [3][7]. For example, you could include: cmd_options: '-J report_json.json -r report_html.html -w report_md.md' 2. SARIF Support: The official action does not natively support SARIF report generation as a direct input or output field [7]. While ZAP has a SARIF report generator add-on [8][9], users typically must configure the underlying ZAP container to generate it via cmd_options or use third-party methods to convert ZAP output to SARIF for GitHub Advanced Security integration [9]. 3. Artifact Handling: By default, the action uploads an artifact named zap_scan [7]. If you generate additional files (like the specific report files requested) within the Docker container during the scan, they will not be automatically uploaded as GitHub workflow artifacts unless you explicitly configure the action or add a subsequent step to upload them using the official actions/upload-artifact action. Version 0.10.0 introduced support for authentication environment variables (ZAP_AUTH_HEADER, ZAP_AUTH_HEADER_VALUE, ZAP_AUTH_HEADER_SITE) [1][2], but did not introduce native support for the specific report formats listed in your query [2].
Citations:
- 1: https://github.com/zaproxy/action-baseline/blob/master/CHANGELOG.md
- 2: zaproxy/action-baseline@22e8c71
- 3: https://github.com/zaproxy/action-baseline/blob/master/README.md
- 4: https://github.com/zaproxy/action-baseline
- 5: https://www.zaproxy.org/docs/docker/baseline-scan/
- 6: https://github.com/zaproxy/zaproxy/blob/0c2975a77e22b28803c910ca42c7e8e4d0df0d8b/docker/zap-baseline.py
- 7: https://github.com/zaproxy/action-baseline/blob/master/action.yml
- 8: https://www.zaproxy.org/docs/desktop/addons/report-generation/report-sarif-json/
- 9: GitHub Code Scanning Integration zaproxy/action-baseline#63
🏁 Script executed:
#!/bin/bash
set -euo pipefail
tool() {
local name="$1"
command -v "$name" >/dev/null 2>&1 && printf '%s\n' "$name"
}
for n in nl head sed grep awk find git; do
echo "tool=$n"
tool "$n"
done
echo "== workflow excerpt =="
if [ -f .github/workflows/zap-security-scan.yml ]; then
awk '{printf "%5d\t%s\n", NR, $0}' .github/workflows/zap-security-scan.yml | sed -n '1,130p'
else
echo "workflow not found"
fi
echo
echo "== zap/report references in workflows =="
grep -RInE "zaproxy/action-baseline|action-baseline|report_json|report_html|report_md|results\.sarif|zap-report|upload-artifact|sarif" .github/workflows .github 2>/dev/null || true
echo
echo "== fetch action metadata/release files =="
python3 - <<'PY'
import urllib.request
for url in [
"https://raw.githubusercontent.com/zaproxy/action-baseline/v0.10.0/action.yml",
"https://raw.githubusercontent.com/zaproxy/action-baseline/v0.10.0/dist/index.js",
"https://raw.githubusercontent.com/zaproxy/action-baseline/v0.10.0/README.md",
]:
print(f"\n--- {url} ---")
try:
data = urllib.request.urlopen(url, timeout=20).read().decode("utf-8", "replace")
except Exception as e:
print(f"ERROR: {e}")
continue
for term in ["report_json", "report_html", "report_md", "sarif", "commandOptions", "cmdOptions"]:
if term in data:
print(term)
# print concise contexts around report paths
for i, line in enumerate(data.splitlines(), 1):
if "report_json" in line or "report_html" in line or "report_md" in line or "sarif" in line:
start=max(1,i-3); end=min(len(data.splitlines()),i+3)
lines=data.splitlines()
for j in range(start,end+1):
print(f"{j:5d}\t{lines[j-1]}")
PYRepository: ritik4ever/stellar-goal-vault
Length of output: 175
Align the report paths with the baseline action output.
zaproxy/action-baseline@v0.10.0 emits report_json.json, report_html.html, and report_md.md; it does not create results.sarif or zap-report.*, so the SARIF upload and JSON enforcement steps will miss the generated reports. Use the emitted filenames for JSON/HTML processing, and add a separate SARIF-generation/conversion step if SARIF is required.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/zap-security-scan.yml around lines 43 - 49, The ZAP
workflow references report filenames that the baseline action does not generate.
Update the JSON and HTML processing steps to use report_json.json and
report_html.html, and update Markdown handling to use report_md.md; if SARIF
remains required, add a separate conversion step that produces the SARIF file
before upload.
|
|
||
| - name: ZAP Baseline Passive Scan | ||
| id: zap | ||
| uses: zaproxy/action-baseline@v0.10.0 |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== workflow excerpt =="
if [ -f .github/workflows/zap-security-scan.yml ]; then
nl -ba .github/workflows/zap-security-scan.yml | sed -n '1,80p'
else
echo "missing .github/workflows/zap-security-scan.yml"
fi
echo
echo "== current action files in GitHub =="
tmpdir="$(mktemp -d)"
curl -fsSL -o "$tmpdir/action-current.yml" https://raw.githubusercontent.com/zaproxy/action-baseline/main/action.yml 2>/dev/null || true
curl -fsSL -o "$tmpdir/action-v010.yml" https://raw.githubusercontent.com/zaproxy/action-baseline/v0.10.0/action.yml 2>/dev/null || true
if [ -s "$tmpdir/action-current.yml" ] || [ -s "$tmpdir/action-v010.yml" ]; then
node - <<'JS'
const fs = require('fs');
const path = require('path');
const files = [
'current-main',
'v0.10.0'
].map(k => ({ k, v: path.join(process.env.HOME === undefined ? '/tmp' : process.env.HOME, 'NOTUSED')) });
console.log("not executed");
JS
fi
for f in "$tmpdir"/action-*.yml; do
[ -s "$f" ] || continue
echo "--- $f ---"
nl -ba "$f" | sed -n '1,80p'
done
echo
echo "== github-hosted runner node version facts =="
python3 - <<'PY'
from datetime import datetime
print("GitHub Actions runner node support window context:")
print("- Node.js 16 was removed from hosted-runners on 2024-11-12.")
print("The current v0.10.0 release pin should be examined for runs.using value.")
PYRepository: ritik4ever/stellar-goal-vault
Length of output: 230
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== workflow excerpt =="
if [ -f .github/workflows/zap-security-scan.yml ]; then
nl -ba .github/workflows/zap-security-scan.yml | sed -n '1,80p'
else
echo "missing .github/workflows/zap-security-scan.yml"
fi
echo
echo "== current action files in GitHub =="
tmpdir="$(mktemp -d)"
curl -fsSL -o "$tmpdir/action-current.yml" https://raw.githubusercontent.com/zaproxy/action-baseline/main/action.yml || true
curl -fsSL -o "$tmpdir/action-v010.yml" https://raw.githubusercontent.com/zaproxy/action-baseline/v0.10.0/action.yml || true
for f in "$tmpdir"/action-*.yml; do
[ -s "$f" ] || continue
echo "--- $f ($(basename "$f")) ---"
nl -ba "$f" | sed -n '1,80p'
done
echo
echo "== Node version references in zaps baseline repository tags =="
python3 - <<'PY'
import subprocess, json, re
tags = []
try:
tags = json.loads(subprocess.check_output(["git", "ls-remote", "--tags", "https://github.com/zaproxy/action-baseline.git"], text=True))
except Exception as e:
print("git ls-remote failed:", e)
for ref, sha in sorted(tags.items()):
if "refs/tags/v0.10.0" in ref:
print(ref, sha)
PYRepository: ritik4ever/stellar-goal-vault
Length of output: 230
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== workflow excerpt =="
if [ -f .github/workflows/zap-security-scan.yml ]; then
awk '{printf "%6d\t%s\n", NR, $0}' .github/workflows/zap-security-scan.yml | sed -n '1,100p'
else
echo "missing .github/workflows/zap-security-scan.yml"
fi
echo
echo "== action.yml files =="
tmpdir="$(mktemp -d)"
curl -fsSL -o "$tmpdir/action-current.yml" https://raw.githubusercontent.com/zaproxy/action-baseline/main/action.yml || true
curl -fsSL -o "$tmpdir/action-v010.yml" https://raw.githubusercontent.com/zaproxy/action-baseline/v0.10.0/action.yml || true
curl -fsSL -o "$tmpdir/action-v011.yml" https://raw.githubusercontent.com/zaproxy/action-baseline/v0.11.0/action.yml || true
for f in "$tmpdir"/action-*.yml; do
[ -s "$f" ] || continue
echo "--- $f ($(basename "$f")) ---"
awk '/^(name|runs):|^[[:space:]]*using:|^[[:space:]]*main:|^ node-version:/' "$f" || sed -n '1,80p' "$f"
done
echo
echo "== package manifests mentioning node versions =="
for p in package.json package-lock.json pnpm-lock.yaml yarn.lock; do
if [ -f "$p" ]; then
echo "--- $p ---"
grep -nEi '"(node|engines|packageManager)|engines:' "$p" || true
fi
doneRepository: ritik4ever/stellar-goal-vault
Length of output: 12632
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== zaps action tags/branch references =="
python3 - <<'PY'
import subprocess, sys
for ref_pat in [
["git", "ls-remote", "--exit-code", "--heads", "https://github.com/zaproxy/action-baseline.git"],
["git", "ls-remote", "--exit-code", "--tags", "https://github.com/zaproxy/action-baseline.git"],
]:
cmd = ref_pat[:]
try:
out = subprocess.check_output(cmd, stderr=subprocess.DEVNULL, text=True)
except subprocess.CalledProcessError as exc:
print(" ".join(cmd), "no_match", file=sys.stderr)
continue
for line in out.splitlines():
if "node" in line.lower() or "main" in line.lower():
print(line)
PY
echo
echo "== report inputs from action manifests =="
tmpdir="$(mktemp -d)"
url='https://raw.githubusercontent.com/zaproxy/action-baseline/${REF}/action.yml'
for ref in v0.11.0 main; do
curl -fsSL -o "$tmpdir/action-$ref.yml" "${url/\${REF}/$ref}" || continue
echo "--- $ref input/output scan summary ---"
awk '
/^inputs:/,/^(outputs:|env:|runs:|branding:|name:)/ {print}
/^outputs:/,/^(env:|runs:|branding:|name:)/ {print}
' "$tmpdir/action-$ref.yml"
doneRepository: ritik4ever/stellar-goal-vault
Length of output: 648
Upgrade the pinned ZAP action off Node16.
zaproxy/action-baseline@v0.10.0 still uses runs.using: node16, and the scan runs on ubuntu-latest where Node16 is no longer supported on hosted runners. Switch to the latest supported release that declares node20 and verify its input/output contract before merging.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/zap-security-scan.yml at line 45, Update the
zaproxy/action-baseline reference in the workflow to the latest supported
release that declares runs.using node20, and verify that its inputs and outputs
remain compatible with the existing scan step before merging.
| if [ -f zap-report.json ]; then | ||
| HIGH_COUNT=$(jq '[.site[]?.alerts[]? | select(.riskcode == "3" or .riskcode == "2")] | length' zap-report.json 2>/dev/null || echo "0") | ||
| echo "## OWASP ZAP Scan Results" >> $GITHUB_STEP_SUMMARY | ||
| echo "" >> $GITHUB_STEP_SUMMARY | ||
| echo "- **Target:** ${{ steps.target.outputs.url }}" >> $GITHUB_STEP_SUMMARY | ||
| echo "- **High-severity alerts:** $(jq '[.site[]?.alerts[]? | select(.riskcode == "3")] | length' zap-report.json 2>/dev/null || echo "0")" >> $GITHUB_STEP_SUMMARY | ||
| echo "- **Medium-severity alerts:** $(jq '[.site[]?.alerts[]? | select(.riskcode == "2")] | length' zap-report.json 2>/dev/null || echo "0")" >> $GITHUB_STEP_SUMMARY | ||
| echo "- **Low-severity alerts:** $(jq '[.site[]?.alerts[]? | select(.riskcode == "1")] | length' zap-report.json 2>/dev/null || echo "0")" >> $GITHUB_STEP_SUMMARY | ||
| if [ "$HIGH_COUNT" -gt 0 ]; then | ||
| echo "❌ **Deploy blocked:** $HIGH_COUNT high-severity or medium-severity findings detected." >> $GITHUB_STEP_SUMMARY | ||
| echo "Please review the ZAP report artifact for details." | ||
| exit 1 | ||
| else | ||
| echo "✅ **No high-severity or medium-severity findings.** Scan passed." >> $GITHUB_STEP_SUMMARY | ||
| fi | ||
| else | ||
| echo "No ZAP JSON report found. Scan results may not be available." | ||
| fi |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Fail closed when the report is missing or invalid.
jq errors are converted to zero counts, and the missing-report branch exits successfully. A failed or malformed scan can therefore appear to pass with no findings. Require a non-empty, schema-valid JSON report and exit nonzero on any parsing or publication failure.
🧰 Tools
🪛 zizmor (1.28.0)
[info] 72-72: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/zap-security-scan.yml around lines 68 - 85, Update the ZAP
report handling around the zap-report.json existence check and jq count
calculations to fail closed: require a non-empty report, validate its
JSON/schema before counting alerts, and treat every jq or summary-publication
failure as an error. Ensure malformed or missing reports and failed writes to
GITHUB_STEP_SUMMARY exit nonzero instead of reporting a successful scan, while
preserving the existing blocking behavior for high- or medium-severity findings.
|
Hi @muizbolaji4, This PR could not be merged because it has merge conflicts with the target branch. Please resolve the merge conflicts, push the updated changes, and the PR can be reviewed and merged. Thank you! |
1 similar comment
|
Hi @muizbolaji4, This PR could not be merged because it has merge conflicts with the target branch. Please resolve the merge conflicts, push the updated changes, and the PR can be reviewed and merged. Thank you! |
Overview
This PR adds an OWASP ZAP Baseline (Passive) Security Scan that automatically runs against the staging deployment. The scan detects common web vulnerabilities passively without sending malicious payloads, blocks the deployment if high-severity findings are discovered, and uploads the scan report as a downloadable CI artifact.
Related Issue
Closes #628
Changes
OWASP ZAP Security Scan Workflow
.github/workflows/zap-security-scan.ymldeployment_statusevent) or manually viaworkflow_dispatch.STAGING_URLrepository secret.fail_action: true) if any alerts of any severity are detected.zap-html-report).target_urlinput parameter.Acceptance Criteria
deployment_statustrigger withstate == 'success'filterfail_action: true+ explicit jq-based check exits with code 1zap-html-reportartifactworkflow_dispatchwith optionaltarget_urlinputVerification
The workflow can be verified by:
STAGING_URLrepository secret to the staging deployment URL.Summary by CodeRabbit
New Features
Bug Fixes