🛡️ WinSecAudit v2
A CLI-first Windows Security Misconfiguration Auditor
Weighted scoring · Threat intelligence enrichment · Multi-format reporting · Drift detection
✨ Overview
WinSecAudit v2 audits Windows 10/11 machines for common security misconfigurations, assigns a weighted risk score, enriches every finding with live CVE threat intelligence, and produces boardroom-ready reports in four formats. It's built as a plugin system — every check is a self-contained class auto-discovered at runtime, so extending the suite requires zero wiring.
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 39 CHECKS │ ──▶ │ SCORING │ ──▶ │ THREAT INTEL│ ──▶ │ REPORTS │
│ (PowerShell) │ │ (weighted) │ │ NVD+EPSS+KEV │ │ PDF/DOCX/CSV │
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
📚 Table of Contents
- 🚀 Quick Start
- ⚙️ Installation
- 🧰 Commands
- 🔍 Security Checks
- 🎛️ Profiles
- 📊 Scoring & Grading
- 🌐 Threat Intelligence
- 📄 Output Formats
- 🧪 Testing
- ⚠️ Known Limitations
- 📂 Project Structure
- 🤝 Contributing
🚀 Quick Start
# 1. Clone & install
git clone https://github.com/KavyaJain321/winsecaudit-v2.git
cd winsecaudit-v2
pip install -r requirements.txt
# 2. Run your first audit (open an elevated PowerShell first)
python winsecaudit.py scan
# 3. Export a beautiful PDF report
python winsecaudit.py report --format pdf
💡 Tip: The SQLite database and all caches are created automatically on first run inside
data/. No setup scripts, no migrations.
⚙️ Installation
Requirements
| Requirement | Version |
|---|---|
| Python | 3.11 or newer |
| OS | Windows 10 / 11 (elevated shell for most checks) |
| Dependencies | See requirements.txt |
Setup
git clone https://github.com/KavyaJain321/winsecaudit-v2.git
cd winsecaudit-v2
pip install -r requirements.txt
Optional
# Set your NVD API key to bump rate limit from 5 → 50 req/s
$env:NVD_API_KEY = "your-key-here" # PowerShell
export NVD_API_KEY="your-key-here" # Bash
🧰 Commands
WinSecAudit ships with 6 Typer-powered commands. Run python winsecaudit.py --help to see them all.
scan — Run the full audit
python winsecaudit.py scan [OPTIONS]
| Option | Default | Description |
|---|---|---|
--profile |
basic |
Profile name (basic or hardened) |
--output |
terminal |
Output mode: terminal or json |
--no-threat-intel |
off | Skip NVD / EPSS / CISA KEV enrichment |
report — Generate a formatted report
python winsecaudit.py report [OPTIONS]
| Option | Default | Description |
|---|---|---|
--scan-id |
latest | Specific scan ID to report on |
--format |
terminal |
terminal, pdf, docx, or csv |
list — List saved scans
python winsecaudit.py list [--machine HOSTNAME] [--limit N]
drift — Compare two scans
python winsecaudit.py drift [--scan-a ID] [--scan-b ID]
Defaults to comparing the two most recent scans. Under the hood, drift emulates a FULL OUTER JOIN with two LEFT JOINs + UNION ALL (SQLite doesn't support full outer joins natively).
fix — List or apply remediations
python winsecaudit.py fix --list # show all remediable checks
python winsecaudit.py fix --check "Check Name" # apply the fix
dashboard — Launch the FastAPI read-only dashboard
python winsecaudit.py dashboard
# Opens http://localhost:8080
🔍 Security Checks
39 checks across 8 categories, all auto-discovered via CheckRegistry.discover():
| Category | # | Sample Checks |
|---|---|---|
| 🔐 Authentication | 6 | Password complexity, min length, account lockout, guest account, admin rename |
| 🚪 Access Control | 5 | UAC level, admin shares, AutoRun, WSH, PowerShell execution policy |
| 🌐 Network | 6 | SMBv1 disabled, LLMNR, NetBIOS, WPAD, RDP NLA, NTLMv1 |
| ⚙️ Services | 5 | Print Spooler, Telnet, SNMP, FTP, WinRM |
| 🧬 Kernel | 4 | Secure Boot, DEP, ASLR, Kernel DMA Protection, VBS/HVCI |
| 🖥️ System | 5 | BitLocker, Defender, Firewall, Windows Update, Secure Boot |
| 📝 Logging | 4 | Audit policy, PowerShell module logging, script-block logging, Sysmon |
| 🔑 Credentials | 4 | Credential Guard, LAPS, WDigest disabled, cached logons |
Each check emits an immutable CheckResult with CVE IDs, MITRE ATT&CK technique, CIS benchmark ID, severity, weight, and a ready-to-run remediation command.
🎛️ Profiles
Profiles live in profiles/*.yaml and control three levers:
| Key | Effect |
|---|---|
relaxed_checks |
Named checks whose FAIL is flipped to PASS (with a note). Use for controls intentionally disabled on a machine class. |
weight_overrides |
Override the default weight of any check by name. |
required_checks |
(Reserved) Checks that must pass for the scan to succeed. |
Bundled Profiles
| Profile | Target | Relaxations |
|---|---|---|
basic |
Workstations, developer machines | BitLocker, Secure Boot, NTLMv1, PS exec policy, Kernel DMA |
hardened |
Servers, high-security workstations | None — all checks enforced with higher weights on crypto/creds |
📊 Scoring & Grading
score = (earned_weight / max_possible_weight) × 100
| Status | Contribution |
|---|---|
| ✅ PASS | earns full check weight |
| ❌ FAIL / ERROR | earns 0 |
| ⏭️ SKIPPED | excluded from numerator and denominator |
Edge case: if every check is skipped →
score = 100.0
Grade Bands
| Grade | Score Range | Meaning |
|---|---|---|
| 🟢 A | ≥ 90 | Excellent — hardened baseline met |
| 🟢 B | ≥ 75 | Good — minor gaps |
| 🟡 C | ≥ 60 | Acceptable — remediation recommended |
| 🟠 D | ≥ 40 | Poor — significant exposure |
| 🔴 F | < 40 | Critical — immediate action required |
🌐 Threat Intelligence
Every CVE associated with a failed check is enriched from three sources:
| Source | Data | Rate Limit | Cache TTL |
|---|---|---|---|
| NIST NVD | CVSS score, severity, description | 5/s anon · 50/s with key | 7 days |
| FIRST EPSS | Exploitation probability (0.0–1.0) | None documented | 7 days |
| CISA KEV | Known-exploited flag + ransomware use | Bulk download | 24 hours |
Caches live in data/cve_map.json and data/cisa_kev_cache.json. Enrichment degrades gracefully — a 429 from NVD does not crash the scan; affected CVEs simply ship without TI metadata.
📄 Output Formats
| Format | File | Highlights |
|---|---|---|
| 🖥️ Terminal | (stdout) | Rich-rendered tables · ANSI colour · live progress |
output/<scan_id>.pdf |
Cover page · executive summary · category breakdown · failure deep-dive · "Page N of M" footer | |
| 📘 DOCX | output/<scan_id>.docx |
Word-native styles · FAIL rows bold + italic |
| 📗 CSV | output/<scan_id>.csv |
UTF-8 BOM · scan metadata denormalised per row (SIEM-friendly) |
🧪 Testing
python tests/smoke_test.py
10 standalone tests cover: imports, check discovery, profile loading, scoring math, grade thresholds, DB round-trip, profile relaxation, TI offline cache, CLI entry point, and the full pipeline. No pytest required — exits 0 on success.
WinSecAudit v2 — Smoke Tests
========================================
PASS 01 Import health
PASS 02 Check discovery
PASS 03 Profile loading
PASS 04 Scoring correctness
PASS 05 Grade thresholds
PASS 06 DB round-trip
PASS 07 Profile relaxation
PASS 08 TI cache offline-safe
PASS 09 CLI entry point
PASS 10 Full pipeline
========================================
Results: 10/10 passed
⚠️ Known Limitations
- 🔒 Elevation required — Most PowerShell checks call
secedit,auditpol, or protected registry paths. Run from an elevated terminal. - 🪟 Windows-only checks — All checks are PowerShell/Windows-specific. The DB, scoring engine, and exporters are cross-platform.
- 📉 NVD rate limit — Without an API key, NVD allows 5 req/s. Set
NVD_API_KEYto raise this to 50 req/s. - 📥 CISA KEV download — First scan of the day downloads ~5 MB from
cisa.gov. Subsequent scans within 24 h use the cache.
📂 Project Structure
winsecaudit-v2/
├── winsecaudit.py # Entry point — forces UTF-8 stdout/stderr on Windows
├── requirements.txt
│
├── checks/ # 🔍 Security check plugins (auto-discovered)
│ ├── base.py # BaseCheck, CheckResult, CheckRegistry
│ ├── auth/ # Authentication (6)
│ ├── access/ # Access control (5)
│ ├── network/ # Network (6)
│ ├── services/ # Services (5)
│ ├── kernel/ # Kernel/hardware (4)
│ ├── system/ # System config (5)
│ ├── logging/ # Audit & logging (4)
│ └── credentials/ # Credential protection (4)
│
├── engine/ # ⚙️ Core orchestration
│ ├── runner.py # Parallel check execution
│ ├── scorer.py # Weighted scoring
│ ├── comparator.py # Profile loading & adjustment
│ ├── risk.py # Grade assignment & priority
│ └── threat_intel.py # NVD / EPSS / CISA KEV pipeline
│
├── db/ # 💾 Persistence
│ ├── database.py # SQLAlchemy Core layer
│ └── schema.sql # Reference DDL
│
├── cli/ # 🧰 CLI surface
│ ├── main.py # Typer commands
│ └── output.py # Rich rendering
│
├── dashboard/ # 🌐 FastAPI read-only dashboard
│ ├── app.py
│ ├── models.py
│ └── static/index.html
│
├── exporters/ # 📄 Report generators
│ ├── pdf.py # reportlab Platypus
│ ├── docx_export.py # python-docx
│ └── csv_export.py # csv module
│
├── profiles/ # 🎛️ YAML profiles
│ ├── basic.yaml
│ └── hardened.yaml
│
├── data/ # 🗃️ Runtime data (gitignored)
└── tests/
└── smoke_test.py # ✅ 10 standalone smoke tests
🤝 Contributing
Adding a new check is a three-step affair:
- Create a file under the appropriate
checks/<category>/directory. - Subclass
BaseCheckand set the class attributes (name,category,weight,severity,cve_ids,mitre_technique,cis_id,remediation_cmd,remediation_supported). - Implement
run() -> CheckResult. That's it —CheckRegistry.discover()picks it up on the next scan.
from checks.base import BaseCheck, CheckResult, CheckStatus, Severity, run_powershell
class MyNewCheck(BaseCheck):
name = "My New Check"
category = "System"
weight = 5
severity = Severity.HIGH
remediation_cmd = "Set-ItemProperty ..."
remediation_supported = True
def run(self) -> CheckResult:
value = run_powershell("Get-Something")
return CheckResult(
check_name=self.name,
category=self.category,
status=CheckStatus.PASS if value == "expected" else CheckStatus.FAIL,
system_value=value,
expected_value="expected",
severity=self.severity,
weight=self.weight,
cve_ids=(),
mitre_technique="",
cis_id="",
remediation_cmd=self.remediation_cmd,
remediation_supported=self.remediation_supported,
)
Built with ❤️ for defenders.
Metadata
Release files for winsecaudit 1.1.7
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| winsecaudit-1.1.7.tar.gz | 194.3 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| winsecaudit-1.1.7-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 432.2 kB
Release files / winsecaudit-1.1.7.tar.gz
| Download URL | winsecaudit-1.1.7.tar.gz |
|---|---|
| Size | 194.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0bdffb7350184c6ba580e8ca6366da5dde2eb751e6e19dcfc20313f416ca567f
|
|
BLAKE2b-256 checksum How to use checksums |
dbd21e00a3eb7b44bb176622210ac8273995f086dade6520fb851c9fa193ff2f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Release files / winsecaudit-1.1.7-py3-none-any.whl
| Download URL | winsecaudit-1.1.7-py3-none-any.whl |
|---|---|
| Size | 237.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
57aebafd3dbb10baaaed783995d425a798e8ea7b6a74210834dcdc650bbcc9e0
|
|
BLAKE2b-256 checksum How to use checksums |
4287c9444160280f95b52e79a039398e730c7fc561b2404e6bbc7f46d5cf6ea7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Security for winsecaudit 1.1.7
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.