GitHub Action
Run OWASP Noir in GitHub Actions workflows for endpoint discovery and optional passive security scanning.
Run OWASP Noir in GitHub Actions for endpoint discovery and optional passive security scanning during CI.
Quick Start
Minimal workflow:
name: Noir Security Analysis
on: [push, pull_request]
jobs:
noir-analysis:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Run OWASP Noir
id: noir
uses: owasp-noir/noir@main
with:
base_path: '.'
- name: Display results
run: echo '${{ steps.noir.outputs.endpoints }}' | jq .
base_path: Directory to analyze (-b/--base-path)endpoints: The JSON result, for later steps to process
Inputs
| Name | Description | Required | Default |
|---|---|---|---|
base_path |
Base path to analyze (equivalent to -b/--base-path) |
Yes | . |
url |
Base URL for endpoints (equivalent to -u/--url) |
No | `` |
format |
Output format (plain, yaml, json, jsonl, markdown-table, curl, httpie, oas2, oas3, etc.) |
No | json |
output_file |
Write results to a file (equivalent to -o/--output) |
No | `` |
techs |
Technologies to include (equivalent to -t/--techs) |
No | `` |
exclude_techs |
Technologies to exclude (--exclude-techs) |
No | `` |
passive_scan |
Enable passive security scan (-P/--passive-scan) |
No | false |
passive_scan_severity |
Minimum severity for passive scan (critical, high, medium, low) |
No | high |
use_all_taggers |
Enable all taggers (-T/--use-all-taggers) |
No | false |
use_taggers |
Enable specific taggers (--use-taggers) |
No | `` |
include_path |
Add source file paths to plain output; JSON always has them (--include-path) |
No | false |
verbose |
Verbose output (--verbose) |
No | false |
debug |
Debug output (-d/--debug) |
No | false |
concurrency |
Concurrency level (--concurrency) |
No | `` |
exclude_codes |
Exclude HTTP response codes (comma‑separated) (--exclude-codes) |
No | `` |
status_codes |
Display HTTP status codes for discovered endpoints (--status-codes) |
No | false |
ai_provider |
AI provider prefix or full custom URL (--ai-provider) |
No | `` |
ai_model |
AI model name (--ai-model) |
No | `` |
ai_key |
AI API key (--ai-key) |
No | `` |
ai_agent |
Enable agentic AI workflow (--ai-agent) |
No | false |
probe |
Fire HTTP requests at discovered endpoints (--probe) |
No | false |
probe_via |
Route probes through a proxy URL (--probe-via) |
No | `` |
export_es |
Index endpoints in Elasticsearch or OpenSearch (--export-es) |
No | `` |
export_webhook |
POST endpoint catalog as JSON to a webhook URL (--export-webhook) |
No | `` |
diff_path |
Old code version path for diff analysis (--diff-path) |
No | `` |
diff_ref |
Git revision to diff the checkout against, e.g. the pull request base; fetched if the checkout lacks it (--diff-ref) |
No | `` |
fail_on |
Fail the step (exit code 3) when the diff has any of added, removed, changed, auth-removed (--fail-on) |
No | `` |
no_log |
Hide execution logs and show only results (--no-log) |
No | true |
Notes:
- Pass boolean options as strings (
'true'/'false'). output_fileis relative to the workspace. Set it when a later step needs the report file. The step outputs are filled either way.status_codes,exclude_codes,probeandprobe_vianeedurl. Without it the scan stops with an error.- With
jsonl, the outputs below fold the lines into the same single documentjsonproduces.
Outputs
| Name | Description |
|---|---|
endpoints |
The full Noir result document ({"endpoints":[...],"passive_results":[...],"errors":[...]}) when format is json or jsonl. Empty for other formats |
passive_results |
A JSON array of passive-scan findings when format is json or jsonl. [] when passive scan is off, found nothing, or the format is anything else |
Consuming outputs:
- name: Count endpoints
run: echo '${{ steps.noir.outputs.endpoints }}' | jq '.endpoints | length'
- name: Show passive issues (if enabled)
run: echo '${{ steps.noir.outputs.passive_results }}' | jq '. | length'
Examples
Advanced Scan
name: Noir Passive Scan
on: [push, pull_request]
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Run OWASP Noir with Passive Scanning
id: noir
uses: owasp-noir/noir@main
with:
base_path: 'src'
format: 'json'
passive_scan: 'true'
passive_scan_severity: 'medium'
use_all_taggers: 'true'
verbose: 'true'
output_file: 'noir-results.json'
- name: Process Results
run: |
echo "Endpoints discovered:"
echo '${{ steps.noir.outputs.endpoints }}' | jq '.endpoints | length'
echo "Passive scan findings:"
echo '${{ steps.noir.outputs.passive_results }}' | jq '. | length'
- name: Save detailed results
uses: actions/upload-artifact@v4
with:
name: noir-security-results
path: noir-results.json
Monorepo Matrix
name: Monorepo Noir
on: [push, pull_request]
jobs:
noir:
runs-on: ubuntu-latest
strategy:
matrix:
service: [service-a, service-b, service-c]
steps:
- uses: actions/checkout@v6
- name: Run Noir for ${{ matrix.service }}
id: noir
uses: owasp-noir/noir@main
with:
base_path: '${{ matrix.service }}'
format: 'json'
Attack Surface Review on Pull Requests
Diff the pull request against its base branch, post the result as a comment, and fail the check when a route loses its authentication:
name: Attack Surface Review
on: pull_request
permissions:
contents: read
pull-requests: write
jobs:
noir-diff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Diff the attack surface
uses: owasp-noir/noir@main
with:
base_path: '.'
diff_ref: ${{ github.event.pull_request.base.sha }}
format: 'markdown-table'
output_file: 'noir-diff.md'
fail_on: 'auth-removed'
- name: Comment on the pull request
if: always()
env:
GH_TOKEN: ${{ github.token }}
run: gh pr comment "${{ github.event.pull_request.number }}" --body-file noir-diff.md
actions/checkout fetches a single commit by default. If diff_ref isn't in the checkout, the action fetches that revision before scanning. The step fails with exit code 3 when the diff matches fail_on, and the report file is still written, so the comment step above runs with if: always(). For inline annotations instead of a comment, use format: 'sarif' and upload the file with github/codeql-action/upload-sarif.
Framework-Specific Scans
- uses: owasp-noir/noir@main
with:
base_path: '.'
techs: 'rails' # ruby on rails
passive_scan: 'true'
- uses: owasp-noir/noir@main
with:
base_path: 'src'
techs: 'express' # node.js express
format: 'json'
- uses: owasp-noir/noir@main
with:
base_path: '.'
techs: 'django' # python django
passive_scan: 'true'
passive_scan_severity: 'medium'
Status Code Configuration
Status codes come from live requests, so url must point at a running instance of the app:
- uses: owasp-noir/noir@main
with:
base_path: '.'
url: 'http://localhost:3000'
status_codes: 'true' # include HTTP status codes
exclude_codes: '404,429' # suppress noisy codes
Alternate Formats
- uses: owasp-noir/noir@main
with:
base_path: '.'
format: 'markdown-table' # or: 'curl', 'httpie', 'yaml', 'jsonl', 'oas3'
output_file: 'noir.md'
Best Practices
- Turn on passive scanning (
passive_scan: 'true') to catch security issues early. - Cut noise with
passive_scan_severityandexclude_codes. - JSON output already records each endpoint's source file. For
plainoutput, setinclude_path: 'true'. - Add frameworks with
techswhen auto-detection misses them. Useexclude_techsto drop analyzers you don't need. - Keep results with
actions/upload-artifact.
Troubleshooting
No endpoints found:
- Check that
base_pathpoints to the source directory - Check that the repository uses a supported framework
- Set
techsexplicitly
Output too large or slow:
- Write
format: 'jsonl'tooutput_file(one endpoint per line) - Narrow
base_pathor usetechs/exclude_techs
Hard to diagnose:
- Set
debug: 'true'orverbose: 'true'together withno_log: 'false'.no_logdefaults totrueand hides the logs
HTTP status noise:
- Disable with
status_codes: 'false'or useexclude_codes
Implementation Notes
- The action runs the
ghcr.io/owasp-noir/noirDocker image. The image tag follows the ref you pin:@v1.4.0uses1.4.0,@mainusesmain, and a commit SHA useslatest. If the tag can't be pulled (for example, a branch with no published image), the action falls back tolatestwith a warning. - Each input maps to one CLI flag.
- See supported technologies:
noir list techs