Automated framework for discovering installed packages, identifying vulnerabilities, applying patches with policy-based controls, and generating audit reports across Linux infrastructure.
Inventory → Package Collection → Vulnerability Scan (Trivy + Grype) → Policy Check → Patch → Verify → Report
↑ ↓
Tagged dual-source Rollback on failure
- Ansible 2.12+
- SSH access to target servers (key-based auth recommended)
- Python 3.9+ on control node
- Target servers: Debian/Ubuntu or RedHat/CentOS
- Optional: Ollama for AI-powered patch intelligence (see AI Patch Intelligence)
Three local containers. Ansible talks to them with the containers.podman connection (no SSH inventory):
| Host | Role | What gets scanned |
|---|---|---|
app01 |
JS app | Clones snyk/goof → OS + npm packages |
db01 |
Database | PostgreSQL → OS packages |
os01 |
Baseline OS | Plain Ubuntu → OS packages only |
# One command: lab + AI + full cycle + mail + open dashboard
cp -n .mailtrap.env.example .mailtrap.env # once — fill Mailtrap user/pass
./scripts/run_poc.shThat script starts the Podman lab if needed, ensures Ollama, runs site.yml (AI enrichment + email after report), then opens reports/final/dashboard.html.
Manual steps (same outcome):
ansible-galaxy collection install -r requirements.yml
./demo/start.sh
ansible-playbook -i inventory/podman/hosts.ini site.ymlWhen prompted for HIGH severity approval, type yes. Watch app01 (npm + OS):
ansible-playbook -i inventory/podman/hosts.ini site.yml --limit app01 -vTear down with ./demo/stop.sh. See demo/README.md for details.
Scans run Trivy and Grype by default. Findings are merged and tagged:
| Field | Meaning |
|---|---|
scanners |
["trivy"], ["grype"], or ["grype","trivy"] |
confirmed_by_both |
true when both scanners reported the same CVE/package/version |
Patching: both Trivy and Grype findings can be patched after OS package names are validated against installed apt/dnf packages. Unresolvable scanner names (e.g. Grype’s bare python) are skipped — not passed to apt — so the play does not break. Rollback uses the real installed package version from the host.
Toggle in group_vars/all.yml:
enable_trivy: true
enable_grype: true
scanner_soft_fail: true
patch_validate_os_packages: true # resolve/validate OS names before apt/dnfAliases for common Grype→distro name mismatches live in roles/patching/defaults/main.yml (patch_package_aliases).
ansible.cfg defaults to inventory/hosts.ini. Edit it with your server details:
[appservers]
app01 ansible_host=192.168.1.30
[databases]
db01 ansible_host=192.168.1.20
[osservers]
os01 ansible_host=192.168.1.40Customize patch policy in vars/patch_policy.yml, then run (SSH inventory — not the Podman demo):
# Full workflow (scan → patch → verify → report)
ansible-playbook site.yml
# Scan only (no changes made)
ansible-playbook site.yml --tags scan
# Dry-run patching (shows what would change)
ansible-playbook site.yml --tags patch --check
# Patch a single server
ansible-playbook site.yml --limit app01
# Generate report from existing scan data
ansible-playbook playbooks/report.ymlFor the Podman lab, always pass -i inventory/podman/hosts.ini.
| Playbook | Purpose | Modifies Servers? |
|---|---|---|
playbooks/collect_packages.yml |
Gather installed package lists | No |
playbooks/scan_vulnerabilities.yml |
Run Trivy + Grype (merged findings) | No (installs scanners if missing) |
playbooks/analyze_results.yml |
Aggregate and display scan results | No (runs locally) |
playbooks/patch_system.yml |
Apply patches with rollback | Yes |
playbooks/verify.yml |
Run health checks | No |
playbooks/report.yml |
Generate HTML/JSON audit report (AI when enabled) | No |
site.yml |
Full workflow (all above) | Yes |
Defined in vars/patch_policy.yml:
| Severity | Default Action |
|---|---|
| CRITICAL | Auto-patch immediately |
| HIGH | Pause for administrator approval |
| MEDIUM | Auto-patch when a fix is available |
| LOW | Auto-patch when a fix is available |
Output lands in reports/final/:
dashboard.html— tabs: Overview / Findings / AI insights- JSON — machine-readable for other tools
CLI summary:
python scripts/summarize.py
python scripts/summarize.py --severity CRITICAL,HIGH
python scripts/summarize.py --format csvansible-vulnerability-framework/
├── inventory/hosts.ini # SSH inventory (default in ansible.cfg)
├── inventory/podman/hosts.ini # Podman demo inventory
├── group_vars/all.yml # Scanners, AI, mail defaults
├── demo/ # Vulnerable container lab for local POC
├── playbooks/ # Individual step playbooks
├── roles/
│ ├── package_inventory/ # Collect installed packages
│ ├── vulnerability_scan/ # Trivy + Grype scan + merge
│ ├── ai_enrichment/ # Optional Ollama insights
│ ├── patching/ # Apply patches + handlers
│ ├── verification/ # Health checks
│ ├── reporting/ # HTML/JSON dashboard
│ └── notify_mail/ # Optional Mailtrap/SMTP after report
├── vars/patch_policy.yml # Policy rules + service mappings
├── reports/ # Generated output (do not commit)
├── scripts/
│ ├── run_poc.sh # One-shot POC (lab + AI + cycle + mail)
│ ├── setup_ai.sh # Install/verify Ollama + model
│ ├── test_ollama.sh # AI smoke test
│ ├── merge_scanner_findings.py # Trivy/Grype merge helper
│ └── summarize.py # CLI report aggregator
├── .mailtrap.env.example # Mail credentials template (gitignored copy)
├── site.yml # Main orchestration playbook
└── ansible.cfg # Ansible configuration
If health checks fail after patching, the framework automatically:
- Reverts packages to pre-patch versions
- Restarts affected services
- Re-runs health checks
- Records the server as
rolled_backin the report
Local Ollama can enrich CRITICAL/HIGH packages with purpose, plain-English summary, business impact, services, dependencies, recommendation, and risk assessment. Insights appear in the dashboard AI insights tab (one insight per source:package, reused on related CVE rows). AI never blocks patching; if Ollama is down, reports still generate without insights.
Prefer the POC script (includes AI), or set up AI alone:
./scripts/setup_ai.sh # install Ollama if needed, pull llama3.2:3b, start, test
./scripts/test_ollama.sh # optional re-checkThen run with the inventory you use (Podman demo shown):
ansible-playbook -i inventory/podman/hosts.ini site.ymlDisable with ai_enrichment_enabled: false in group_vars/all.yml, or -e "ai_enrichment_enabled=false".
Defaults in group_vars/all.yml:
ai_enrichment_enabled: true
ollama_endpoint: "http://localhost:11434/api/generate"
ollama_model: "llama3.2:3b"
ollama_timeout: 60Full knobs and troubleshooting: docs/AI.md.
Cron examples (SSH inventory default):
# Nightly scan (no patching)
0 2 * * * cd /path/to/ansible-vulnerability-framework && ansible-playbook site.yml --tags scan,report
# Weekly full patch run (Sundays at 3 AM)
0 3 * * 0 cd /path/to/ansible-vulnerability-framework && ansible-playbook site.yml