- Python 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| prompts | ||
| skills/security-audit | ||
| .gitignore | ||
| package.json | ||
| PLAN.md | ||
| README.md | ||
pi-security-audit
A pi package adding /security-audit — a phased, read-only LAN security audit:
host discovery → TCP port scan → service/OS fingerprinting → CVE matching → a self-contained
interactive HTML report of your local network. Stdlib-only Python 3, no sudo, Linux only.
Install
# from git (pinned to the v1.0.0 tag)
pi install git:https://git.cyfrowynomada.com/CyfrowyNomada/pi-security-audit@v1.0.0
# or from a local checkout
pi install ~/Projects/pi-security-audit
Then run /reload in an active pi session; /security-audit appears in command completion
(pi list shows the package).
Usage
/security-audit # derive the target network from the default route, gate, audit
/security-audit 10.0.0.0/24 # explicit target network
/skill:security-audit 10.0.0.0/24 # force form if the thin template fails to route
| Argument | Required | Meaning |
|---|---|---|
[cidr] |
no | Target IPv4 network. Default: derived from ip route show default. Undecidable → the runbook asks you for it (the discovery script exits 2 with a message otherwise). |
The audit workdir defaults to ./security-audit/ — evidence in <out>/evidence/, report in
<out>/report/index.html. Ask for a different one and all five phases will use it; re-running
against the same workdir resumes (completed phases are skipped).
Authorization & scope policy
- Own networks only. Audit only LANs you own or administer. The skill's step-0 scope gate shows the exact CIDR, the scannable host count, and the full rate-cap table, and requires your explicit confirmation before a single packet is sent.
- Loopback safe-test.
127.0.0.1/32targets this host only and bypasses the confirmation gate — the built-in way to try the pipeline safely. It expects a local sshd on 127.0.0.1:22 (the fingerprint phase asserts an SSH banner there). - Hardcoded, unloosenable caps — 32 concurrent probes, ≥250ms sleep between port-scan batches, 10s hard timeout per fingerprint probe, 100-request CVE budget with ≥1s spacing. No CLI flag can loosen them; they are constants in the scripts.
- Read-only. The scripts connect, read banners, and write evidence locally; they never modify remote hosts and never call xdg-open.
Phases
- discover —
scripts/discover.pyFinds this host's IP (UDP sockname) and the gateway (default route), both overridable, then sweeps the network for live hosts via unprivileged ICMP ping sockets,/proc/net/arpdeltas, and pre-scan residents, cross-verified by TCP touches on 22/80/135/139/445. Writesevidence/hosts.json(withmeta: {self_ip, gw_ip, cidr, platform}) plus a sample re-verification agreement score. Exits 2 with a clear message if the CIDR is undecidable or the platform is not Linux. - ports —
scripts/ports.pyScans every discovered host against a pinned list of 39 common TCP ports under hard caps (4 sockets/host, 32 global, ≥250ms between per-host batches, 2s timeouts, one 5s extra pass for hosts showing 0 open). Anss-based anchor cross-checks the audit host's own listeners, degrading to "n/a" ifssis unavailable. Writesevidence/ports.json. - finger —
scripts/finger.pyFingerprints each open port: banners, TTL-based OS buckets, SMB minimum-protocol negotiation, RDP exposure hints; individual probe families degrade to "unavailable" rather than failing the phase. A built-in dry gate fingerprints 127.0.0.1:22 first and asserts an SSH banner (needs local sshd). Writesevidence/services.json. - cve —
scripts/cve.pyMatches fingerprinted products against cve.circl.lu with version-range containment checks, per-URL caching underevidence/cve-cache/, and verbatim raw responses archived inevidence/cve/. Offline or throttled → entries are honestly marked "CVE check unavailable";--selftestexercises the containment logic offline. Writesevidence/cve-summary.json. - report —
scripts/report_html.pyRenders<out>/report/index.html, a self-contained interactive report, from the evidence; missing phases degrade to "pending" badges — never a fake all-clear. Prints the report path.
Requirements
- Linux (needs
/proc/net/arpand unprivileged SOCK_DGRAM ICMP; other platforms exit loudly with code 2 and recordevidence/platform-guard.json) - Python ≥ 3.10 — stdlib only, no pip installs, no sudo
- Internet access only for phase 4; phases 1–3 and 5 are LAN-local / offline
Smoke test
Quick offline checks: python3 skills/security-audit/scripts/cve.py --selftest, and the loopback
--dry-run gates on discover.py / ports.py. A full loopback end-to-end run
(/security-audit 127.0.0.1/32, sshd on :22 required) is exercised by the T3 verification pass
(see PLAN.md, Milestone M2).
Troubleshooting
- "could not derive the LAN network" (exit 2) — pass an explicit target:
/security-audit 10.0.0.0/24. - "unsupported platform" (exit 2) — run on a Linux host (e.g. the pi machine itself); the
condition is recorded in
evidence/platform-guard.json. - "CVE check unavailable" badge — normal when offline or cve.circl.lu is unreachable; it is not an all-clear.
- A phase reported "already complete, skipping" — resume semantics: valid evidence in the
same
--outshort-circuits the phase; re-run that script with--forceto redo it. - finger aborts at its dry gate — no sshd listening on 127.0.0.1:22; start one first.