<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ansible Security Scanner: SAST for Playbooks, Roles, Collections :: Ansible Security Scanner</title><link>https://cpeoples.github.io/ansible-security-scanner/index.html</link><description>Static SAST scanner for Ansible playbooks, roles, collections, and inventories. Detects malicious code, RCE, command and template injection, hardcoded credentials, supply-chain risk, unauthorized cloud access, lateral movement, and reverse shells. Outputs SARIF, CycloneDX SBOM, GitLab SAST, JUnit, JSON, HTML, and Markdown reports with remediation guidance. Findings map to CWE, OWASP Top 10, OWASP ASVS, MITRE ATT&amp;CK, NIST, and CIS.</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Fri, 05 Jun 2026 03:21:14 +0000</lastBuildDate><atom:link href="https://cpeoples.github.io/ansible-security-scanner/index.xml" rel="self" type="application/rss+xml"/><item><title>About</title><link>https://cpeoples.github.io/ansible-security-scanner/about/index.html</link><pubDate>Fri, 05 Jun 2026 03:21:14 +0000</pubDate><guid>https://cpeoples.github.io/ansible-security-scanner/about/index.html</guid><description>The project Ansible Security Scanner is a static analysis (SAST) tool for Ansible playbooks, roles, collections, task files, vars, and inventories. It focuses on real security signals: malicious code, RCE, command and template injection, hardcoded credentials, supply-chain risk, unauthorized cloud access, lateral movement, and reverse shells. Style and linting concerns are intentionally left to other tools.</description></item><item><title>Dashboard</title><link>https://cpeoples.github.io/ansible-security-scanner/dashboard/index.html</link><pubDate>Fri, 05 Jun 2026 03:21:14 +0000</pubDate><guid>https://cpeoples.github.io/ansible-security-scanner/dashboard/index.html</guid><description>Scanner Overview Metric Value Total Rules 1100 Pattern Categories 31 CRITICAL Rules 412 HIGH Rules 536 MEDIUM Rules 132 LOW Rules 19 Rules by Category Category Rules Critical High Medium Low Unauthorized Cloud &amp; Infrastructure Access 144 60 71 13 0 Operational Security 127 40 69 14 4 Supply Chain Integrity 121 35 71 13 2 Hardcoded Credentials 87 44 37 5 1 Malicious Activity 62 42 16 4 0 Insecure Communication 61 12 44 5 0 System Compromise 56 27 22 7 0 Offensive Security Tools 49 39 10 0 0 Unsafe Permissions 49 5 25 12 6 Kubernetes Insecure Pod/Workload Spec 37 5 18 11 3 AI / ML Security 34 15 17 2 0 Ansible-Specific Security 30 13 16 1 0 Command Injection 27 2 19 6 0 Reverse Shell Detection 19 19 0 0 0 Template Injection 19 3 11 4 1 Data Exfiltration 18 2 10 6 0 Tunneling, Proxying &amp; Network Exposure 18 8 10 0 0 Privilege Escalation 16 5 9 2 0 Ansible Lateral Movement &amp; Abuse 15 3 10 2 0 Ansible Best Practice Hygiene 14 1 7 4 2 Anti-Forensics &amp; Evidence Tampering 14 7 6 1 0 Webshell Deployment 14 11 3 0 0 External URL 11 0 2 9 0 Jinja2 / Lookup RCE 10 2 6 2 0 Environment Hijacking 9 1 6 2 0 Encoding, Obfuscation &amp; Evasion 9 0 8 1 0 Data Destruction &amp; Ransomware 8 8 0 0 0 Binary Planting &amp; Execution Hijacking 7 2 5 0 0 Dangerous Module 5 0 1 4 0 Variable Injection 5 0 4 1 0 Webhook Exposure 5 1 3 1 0</description></item><item><title>CLI Reference</title><link>https://cpeoples.github.io/ansible-security-scanner/cli/index.html</link><pubDate>Fri, 05 Jun 2026 03:21:14 +0000</pubDate><guid>https://cpeoples.github.io/ansible-security-scanner/cli/index.html</guid><description>ansible-security-scanner [OPTIONS] Scan: -d, --directory DIR Directory to scan (default: .) --files FILE [FILE ...] Scan specific files only --changed-files VAR Scan only changed files (CI/CD variable or list) --allowlist PATH Path to allowlist YAML (default: .security-scanner-allowlist.yml) Output: -f, --format FORMAT markdown | json | xml | yaml | csv | html | junit | sarif | gl-sast | cyclonedx | sbom (inferred from --output extension if omitted) -o, --output FILE Save report to file (default: console) --output-per-file Write one report per scanned YAML file instead of one aggregate. --output must be a directory; defaults to ./security-reports/ if omitted. Scanned paths are preserved under the output dir and suffixed with the format's canonical extension (e.g. roles/web/tasks.yml -> roles/web/tasks.yml.md). Not supported with cyclonedx/sbom (aggregate-only). --exit-zero Always exit 0 (report but don't fail builds) -v, --verbose Enable debug logging Engine upgrades (opt-in): --fix Dry-run autofix: annotate findings with a unified diff patch for high-confidence rules (no files are modified) --fix-output PATH With --fix: write the concatenated patches to a file for easy review in `git apply --check` --scan-git-history Scan historical versions of tracked files for leaked secrets --git-history-max-commits N With --scan-git-history: limit history depth (default 50) --compliance TAG[,TAG...] Filter findings to specific CIS control tags --compliance list Print every compliance tag the rule bundle exposes --dedup-across-files Collapse identical findings (same rule + same normalized snippet) across multiple files into a single representative finding. Other affected locations are preserved on the representative's `duplicates` list (visible in JSON / YAML / SARIF / Markdown / HTML). Suppression controls (inline, on-line): --show-suppressed Include suppressed findings in the report (default: hidden) --no-suppressions Ignore every inline `# nosec` / `# noqa` directive - for release gates and audit runs where authors cannot silence findings --fail-on-suppressed Non-zero exit if *any* finding in the tree is suppressed --max-suppressions N Non-zero exit if more than N findings are suppressed (budget gate) Filtering &amp; performance: --severity LEVEL Filter the report to findings at or above LEVEL (CRITICAL > HIGH > MEDIUM > LOW). Default: no filter. Exit codes still gate on HIGH / CRITICAL regardless of this flag. --select RULE[,RULE...] Run ONLY the listed rules. Accepts comma- separated rule_ids and fnmatch globs (e.g. `aws_*,hardcoded_password`). Filtering happens at scan time so single-rule runs are fast on large repos. Unknown rule_id -> exit 2. --ignore RULE[,RULE...] Drop the listed rules from the scan. Same syntax as --select. When both flags are given, --select defines the universe and --ignore carves out of it. --list-rules Print every known rule_id (one per line, sorted) and exit. Pipe-friendly: header goes to stderr, rule_ids to stdout. Combine with `grep` / `fzf` to discover what to --select. --list-rules-detailed Like --list-rules but emits a TSV of `rule_id&lt;TAB>severity&lt;TAB>category&lt;TAB>title` so operators can disambiguate findings whose display title is shared by more than one rule_id. Synthetic / code-emitted rule_ids carry `&lt;synthetic>` placeholders for severity/category/title. --jobs N, -j N Run the per-file scan stage on N worker threads (default: 1 = sequential). The downstream sort makes the final report bit-for-bit identical to a serial run, so safe to set high (e.g. 4-8) on large repos. MR / PR commenting (CI/CD): --github-comment, --gh-comment Post/update a concise findings summary on the current GitHub PR. Must run in a pull_request workflow. Token: GITHUB_TOKEN / GH_TOKEN / ANSIBLE_SEC_SCANNER_GITHUB_TOKEN. --gitlab-comment, --gl-comment Post/update a concise findings summary on the current GitLab MR. Must run in a merge_request_event pipeline. Works against self-hosted instances via CI_SERVER_URL. Token: GITLAB_TOKEN / CI_JOB_TOKEN / ANSIBLE_SEC_SCANNER_GITLAB_TOKEN. --mr-comment-full-report PATH Full-report artifact location (default: security-reports/report.md). The MR comment links to this file. --no-mr-comment-scope-changed-files Disable the default "scan only the MR's changed YAML files" behaviour - scan the full --directory even inside an MR pipeline. --inline-comments Also post per-finding inline review threads on each offending diff line (GitLab Discussions API / GitHub GraphQL). Off-diff findings fall back to file-level threads. Idempotent on re-runs. --no-inline-comments Disable inline review threads (default). Exit codes Code Meaning 0 No CRITICAL or HIGH findings (or –exit-zero passed) 1 One or more HIGH-severity findings 2 One or more CRITICAL findings, a suppression-gate failure, or a CLI-usage error (e.g. –output would overwrite an input file, –output-per-file used with cyclonedx) Multi-file Ansible projects The scanner is designed for realistic multi-file Ansible trees - playbooks, roles, group_vars/, host_vars/, inventory files, Jinja2 templates (*.j2), ansible.cfg, requirements.yml, meta/main.yml, execution-environment.yml, and bindep.txt are all auto-discovered and routed to the correct rule set.</description></item><item><title>Environment</title><link>https://cpeoples.github.io/ansible-security-scanner/environment/index.html</link><pubDate>Fri, 05 Jun 2026 03:21:14 +0000</pubDate><guid>https://cpeoples.github.io/ansible-security-scanner/environment/index.html</guid><description>The scanner reads the following environment variables. Tokens are only read from env vars - never from CLI flags - so they never land in shell history, CI logs, or –help output.
Authentication (MR/PR commenting) Variable Used by Purpose ANSIBLE_SEC_SCANNER_GITHUB_TOKEN –gh-comment Highest-precedence GitHub token. Use when you want a scanner-specific token separate from the workflow’s default GITHUB_TOKEN. GITHUB_TOKEN –gh-comment The default token GitHub Actions injects into every workflow. Needs pull-requests: write. GH_TOKEN –gh-comment Alternative name some workflows use; same semantics as GITHUB_TOKEN. ANSIBLE_SEC_SCANNER_GITLAB_TOKEN –gl-comment Highest-precedence GitLab token. GITLAB_TOKEN –gl-comment Personal access token or project access token with api scope. CI_JOB_TOKEN –gl-comment The token GitLab CI injects automatically. Works for the project’s own MRs without extra setup. Platform detection (set automatically by GitHub Actions / GitLab CI) The scanner detects which platform it’s running on by reading these. You don’t set them manually; they’re populated by your CI runner.</description></item><item><title>Python API</title><link>https://cpeoples.github.io/ansible-security-scanner/api/index.html</link><pubDate>Fri, 05 Jun 2026 03:21:14 +0000</pubDate><guid>https://cpeoples.github.io/ansible-security-scanner/api/index.html</guid><description>The package exposes a stable Python API for callers that want to embed the scanner instead of shelling out. Everything in ansible_security_scanner.all__ is considered public surface and follows semantic versioning.
This page is the full reference. For day-to-day CLI usage see CLI Reference; for output shapes see Output Formats.
Quick start 1 2 3 4 5 6 7 from ansible_security_scanner import AnsibleSecurityScanner, JSONFormatter scanner = AnsibleSecurityScanner(directory="ansible/") report = scanner.scan_directory() print(f"{len(report.findings)} findings; score {report.security_score.overall_score}/100") print(JSONFormatter().format(report)) Two things to notice:</description></item><item><title>Output Formats</title><link>https://cpeoples.github.io/ansible-security-scanner/output-formats/index.html</link><pubDate>Fri, 05 Jun 2026 03:21:14 +0000</pubDate><guid>https://cpeoples.github.io/ansible-security-scanner/output-formats/index.html</guid><description>The scanner supports 10 output formats, selected with –format .
Format –format value File extension Primary use case Markdown (default) markdown .md Console output, MR/PR comments, wiki pages JSON json .json Programmatic processing, custom CI dashboards HTML html .html Stakeholder reports (dark/light mode) SARIF 2.1.0 sarif .sarif GitHub Code Scanning, security aggregators GitLab SAST gl-sast / gitlab-sast .json GitLab Security Dashboard, MR security widget JUnit XML junit .xml GitLab/Jenkins reports:junit tab XML (generic) xml .xml Enterprise XML-based tools YAML yaml .yml YAML-driven config pipelines CSV csv .csv Spreadsheets, Excel, data analysis CycloneDX 1.5 SBOM cyclonedx / sbom .cdx.json Dependency-Track, GitHub Dependency Graph, Snyk The CycloneDX output is a full SBOM of the Ansible project: Galaxy collections, roles, pip packages, bindep system packages, and execution-environment container images - each mapped to a standard purl, alongside the scanner’s findings as CycloneDX vulnerabilities[] entries so downstream consumers get both inventory and risk in a single document.</description></item><item><title>Allowlist</title><link>https://cpeoples.github.io/ansible-security-scanner/allowlist/index.html</link><pubDate>Fri, 05 Jun 2026 03:21:14 +0000</pubDate><guid>https://cpeoples.github.io/ansible-security-scanner/allowlist/index.html</guid><description>Some findings are expected for approved playbooks. The allowlist lets you suppress specific rules for specific files without disabling the scanner.
Configuration Edit .security-scanner-allowlist.yml (next to main.py), or pass a custom path with –allowlist /path/to/config.yml.
1 2 3 4 5 6 7 8 9 10 11 12 13 allowlist: # Suppress specific rules for a file - file: ansible/deploy_prod.yml rules: - direct_sqs_send_message - direct_sqs_queue_url reason: "Uses approved API Gateway endpoint, not direct SQS" # Suppress ALL rules for a legacy file - file: ansible/legacy_playbook.yml rules: - "*" reason: "Legacy playbook pending migration; tracked in TICKET-1234" How it works file: is the path relative to the scan directory rules: is a list of rule IDs to suppress. Use "*" to suppress everything. reason: is logged at INFO level for audit trail (visible with –verbose) Suppressed findings do not count toward the security score or exit code Suppressed findings are logged at INFO level so they remain auditable Finding rule IDs Run the scanner with –format json and check each finding’s rule_id field:</description></item><item><title>CI/CD</title><link>https://cpeoples.github.io/ansible-security-scanner/ci-cd/index.html</link><pubDate>Fri, 05 Jun 2026 03:21:14 +0000</pubDate><guid>https://cpeoples.github.io/ansible-security-scanner/ci-cd/index.html</guid><description>GitLab CI 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ansible_security_scan: stage: test image: python:3.12-slim before_script: - pip install ansible-security-scanner script: - ansible-security-scanner --directory ansible --changed-files "$CHANGED_FILES" --format junit --output reports/security-results.xml --allowlist .security-scanner-allowlist.yml artifacts: reports: junit: reports/security-results.xml expire_in: 30 days GitHub Actions 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 - uses: actions/setup-python@v5 with: python-version: "3.12" - name: Install Ansible Security Scanner run: pip install ansible-security-scanner - name: Ansible Security Scan run: | ansible-security-scanner \ --directory ansible \ --format sarif --output results.sarif - name: Upload SARIF results uses: github/codeql-action/upload-sarif@v3 with: sarif_file: results.sarif GitLab CI - Security Dashboard integration Use the gl-sast format to populate GitLab’s native Security Dashboard and the MR security widget. The artifacts:reports:sast keyword tells GitLab to ingest the JSON report - no extra tooling or analyzer image required.</description></item><item><title>PR/MR Comments</title><link>https://cpeoples.github.io/ansible-security-scanner/mr-pr-comments/index.html</link><pubDate>Fri, 05 Jun 2026 03:21:14 +0000</pubDate><guid>https://cpeoples.github.io/ansible-security-scanner/mr-pr-comments/index.html</guid><description>Post (or update) a concise, reviewer-friendly findings summary on the current pull request (GitHub) or merge request (GitLab). The comment is edited in place on every subsequent scan of the same branch - no comment threads, no duplicate noise - and flips to a “All security findings resolved” banner (citing how many findings were cleaned up and which rule IDs) once the MR is clean.
Key properties Platform is detected from CI env vars only. No config file, no flag, no network probe - just GITHUB_ACTIONS / GITHUB_REPOSITORY / GITHUB_REF (GitHub) or CI_SERVER_URL / CI_PROJECT_ID / CI_MERGE_REQUEST_IID (GitLab). Self-hosted GitLab / GitHub Enterprise is transparent. The scanner reads CI_SERVER_URL / GITHUB_SERVER_URL and talks to that API endpoint - works against on-prem instances out of the box. Tokens come from env vars only. Never from CLI flags or config - so tokens never land in shell history, CI logs, or a stray –help. The scanner looks for GITHUB_TOKEN / GH_TOKEN (or a scanner-specific ANSIBLE_SEC_SCANNER_GITHUB_TOKEN) on GitHub, and GITLAB_TOKEN / CI_JOB_TOKEN (or ANSIBLE_SEC_SCANNER_GITLAB_TOKEN) on GitLab. Scan is auto-scoped to the MR’s changed files by default, so the comment only talks about files the MR actually touches. Override with –no-mr-comment-scope-changed-files to scan the full –directory inside an MR pipeline. Big MRs degrade gracefully. A Dashboard + Drilldown renderer keeps comments under GitHub’s 65 536-character limit even on thousand-finding MRs: the top rules get full detail, the rest collapse into a summary line pointing at the artifact report. Warn-and-continue. A flaky API call or missing env var logs a warning and returns - the scanner’s exit code stays driven by findings, never by comment-posting failures. Full-report artifact is always written to security-reports/report.md (overridable with –mr-comment-full-report PATH). The MR comment links to this artifact so reviewers can click through from the dashboard view. Short aliases (–gh-comment / –gl-comment) are equivalent to the long forms and exist because CI YAML tends to be long enough already.</description></item><item><title>Custom Patterns</title><link>https://cpeoples.github.io/ansible-security-scanner/custom-patterns/index.html</link><pubDate>Fri, 05 Jun 2026 03:21:14 +0000</pubDate><guid>https://cpeoples.github.io/ansible-security-scanner/custom-patterns/index.html</guid><description>Create a YAML file in src/ansible_security_scanner/patterns/ with this structure:
1 2 3 4 5 6 7 8 9 10 11 12 name: "My Custom Patterns" author: "Your Name" description: "What these patterns detect" patterns: - id: "my_custom_rule" category: "my_category" severity: "HIGH" # CRITICAL, HIGH, MEDIUM, LOW title: "Human-Readable Title" description: "What this detects and why it matters" regex: "the.*regex.*to.*match" recommendation: "How to fix it" Pattern files are auto-discovered on startup. No code changes needed.</description></item><item><title>Scoring</title><link>https://cpeoples.github.io/ansible-security-scanner/scoring/index.html</link><pubDate>Fri, 05 Jun 2026 03:21:14 +0000</pubDate><guid>https://cpeoples.github.io/ansible-security-scanner/scoring/index.html</guid><description>Security scores are calculated based on issue severity:
Severity Points Deducted CRITICAL -15 each HIGH -8 each MEDIUM -3 each LOW -1 each Score Range Rating 90-100 Excellent 75-89 Good 60-74 Fair 40-59 Poor 20-39 Critical 0-19 Severe</description></item><item><title>Testing</title><link>https://cpeoples.github.io/ansible-security-scanner/testing/index.html</link><pubDate>Fri, 05 Jun 2026 03:21:14 +0000</pubDate><guid>https://cpeoples.github.io/ansible-security-scanner/testing/index.html</guid><description>The test suite validates every shipped rule with 100% coverage:
1 2 3 4 5 6 7 8 9 10 11 # Full pytest suite (includes multi-file integration + CLI UX tests): python task.py test # Filtered run: python task.py test -- -k output_per_file # Or directly with pytest (virtualenv must have dev deps): pytest tests/ -v # Or run the integration suite standalone: python tests/test_integration.py What the tests verify bad_example.yml – triggers every single shipped rule clean_example.yml – produces zero findings (false-positive check) multi_example_bad/ – 6-file role fixture that exercises cross-file taint, role-task AST parsing, and deterministic finding counts across a realistic layout multi_example_clean/ – 6-file hardened role fixture, zero findings expected (multi-file false-positive guard) No duplicate pattern IDs across all YAML files All regexes compile without errors Category field matches filename for every pattern CLI behaviours – format inference from –output extension, per-file report mode, –output overwrite protection, smart default output directory</description></item><item><title>Limitations</title><link>https://cpeoples.github.io/ansible-security-scanner/limitations/index.html</link><pubDate>Fri, 05 Jun 2026 03:21:14 +0000</pubDate><guid>https://cpeoples.github.io/ansible-security-scanner/limitations/index.html</guid><description>This is a static, pattern-based scanner. It analyzes YAML text and structure without executing playbooks. You should be aware of what it can and cannot do.
What it catches:
Known-bad patterns, commands, modules, and configurations Hardcoded secrets, credentials, and tokens Common evasion techniques (encoding, obfuscation, variable indirection) Structural issues in parsed YAML (missing no_log, ignore_errors on security tasks) What it cannot catch:
Runtime behavior - dynamically constructed commands, values resolved at execution time via lookups/facts/registered variables, or logic gated behind conditionals Semantic intent - it cannot distinguish between a legitimate aws s3 cp in an approved deployment role and the same command used maliciously Custom obfuscation - novel encoding schemes, steganographic payloads, or patterns not covered by existing rules External content at runtime - the scanner flags risky-looking include_role / import_tasks from URLs and unpinned Galaxy installs, but cannot inspect the content of files fetched or rendered at execution time Off-tree data flow - cross-file taint tracking works across the scanned set (registered vars, set_fact, include_vars, host/group vars), but data that originates outside that set (controller env at execution, dynamic inventories, external lookups) cannot be tracked Recommendations:</description></item><item><title>Releasing</title><link>https://cpeoples.github.io/ansible-security-scanner/releasing/index.html</link><pubDate>Fri, 05 Jun 2026 03:21:14 +0000</pubDate><guid>https://cpeoples.github.io/ansible-security-scanner/releasing/index.html</guid><description>Releases are fully automated via .github/workflows/scanner-release.yml. The workflow uses PyPI Trusted Publishing (OIDC) - there are no long-lived API tokens stored in GitHub secrets.
One-time setup Create the project on PyPI and TestPyPI (blank projects are fine). On each, add a Trusted Publisher pointing at this repo: Owner: cpeoples Repository: ansible-security-scanner (or wherever this lives) Workflow name: scanner-release.yml Environment name: pypi (for PyPI) / testpypi (for TestPyPI) In GitHub -> Settings -> Environments, create two environments with matching names: pypi and testpypi. Add required reviewers to pypi if you want a manual approval gate before production uploads. Cutting a release 1 2 3 4 5 6 7 # 1. Make sure main is green (scanner-ci.yml passed). # 2. Tag the release commit. The version in the tag is authoritative - # hatch-vcs reads it and stamps it into the wheel. git tag scanner-v1.2.3 git push origin scanner-v1.2.3 # 3. On GitHub -> Releases -> Draft a new release -> pick the tag -> Publish. Publishing the Release triggers:</description></item><item><title>Security Patterns</title><link>https://cpeoples.github.io/ansible-security-scanner/patterns/index.html</link><pubDate>Fri, 05 Jun 2026 03:21:14 +0000</pubDate><guid>https://cpeoples.github.io/ansible-security-scanner/patterns/index.html</guid><description>The Ansible Security Scanner ships with pattern plugins organized by threat category. Each YAML file in src/patterns/ is auto-discovered at scan time – no code changes needed to add new rules.
Browse the categories below to see every rule, its severity, and what it detects.</description></item></channel></rss>