Apply Department of Defense security checklists to your own systems without a compliance specialist.
An open-source, AI-enabled toolkit consisting of AI skills and scripts that turns any DISA STIG into a plain-English plan, explains every rule, checks a machine against it, drafts a Plan of Action and Milestones, and drafts remediation scripts — with a human at every gate. Free, MIT licence, no account, no API key. All four modules (stigprep, stigscan, stigassess, stigharden) ship as working code in this repository.
New here? Read the two-page overview with a worked example: docs/overview.pdf.
The US Department of Defense publishes free cybersecurity checklists called STIGs (Security Technical Implementation Guides) for almost every common system — Windows, Linux, macOS, SQL Server and hundreds more. Each one lists a few hundred settings. Anyone can download them.
Almost nobody outside government uses them. Each guide is a few hundred rules written for a security auditor; applying one means reading every rule, understanding it, ranking it by risk, and translating it into a change on a real machine. That takes a specialist, and most organisations do not have one. So the checklists sit unused, and the systems stay less secure than they could be for free.
This toolkit does the interpreting. Scanners like OpenSCAP can already tell you that you have 169 findings; this project handles the last mile — what each finding actually asks of you, which fixes are safe to make today, and what could go wrong — with explicit flags for the rules where a human must decide.
| If you are… | This gives you… |
|---|---|
| An IT generalist at a school district, clinic, small bank, municipal agency, or any organisation without a security team | A ranked, plain-English plan for securing each system, and a way to check your work |
| A managed service provider looking after many small clients | One repeatable method across Windows, Linux, macOS and SQL Server, with a dated report per client per scan |
| A sysadmin at a DoD contractor or federal agency where STIG compliance is mandatory | The translation from guide to tracker done for you, and evidence for the accreditation package that says exactly what was and was not evaluated |
| A security engineer who already knows STIGs | A parser that reads any XCCDF as DISA ships it, explanation sets you can reuse, and a scanner whose safety model you can audit line by line |
| Someone answering "are our systems secure?" to a board, an auditor, a customer or a cyber-insurer | A recognised baseline (each rule maps to NIST 800-53 through DISA's own identifiers) and a report to hand over instead of an opinion |
- A week of specialist work in a few minutes.
parseturns a 300-page guide into a tracker: one row per rule, severity ranked, full check and fix text, as Excel, CSV, JSON or Markdown. - Every rule explained. What it makes you do and why, a rating (quick win / needs a policy pushed out / needs a decision / can break things), whether it can be scripted, and what could go wrong. Two scoped totals, not one ambiguous number: 1,440 rules explained across seven STIGs in
annotations/; 1,338 rules in the five packaged check packs (the same five OS guides; SQL Server is explained but has no check pack). Thestig-explainskill produces more with the AI assistant you already have. - A scan you can trust.
stig-scanchecks a machine against its guide. A normal scan runs only checks a named person has read and approved; approval freezes a fingerprint of the exact command; a safety gate refuses anything that could change the system; and the report says first how many rules were actually evaluated. The command line can force an unreviewed run with--include-unreviewed(the local page has a matching optional checkbox); those results are labeled untrusted and are not a normal approved scan or accreditation evidence. The program never approves a check on a person's behalf. AI may help write a check. It never runs one. - A draft POA&M, not a closed finding.
stig-assessturns scan failures into Plan of Action and Milestones drafts. A named person approves the wording (that opens the item) and is the only one who can close it. A later passing scan does not close anything. - A candidate fix script, not an applied change.
stig-hardendrafts a remediation from a known check shape. Approval freezes the script a person read.applyis a dry-run unless--apply-for-realis passed with--byand--i-have-reviewed, and that flag is refused in CI. The local page's Draft buttons only write files. - Nothing to buy and nothing to sign up for. No dependencies, no API key, no vendor. Works with any AI assistant that supports skills, or with none.
All four modules ship as working code in this repository (covered by the unit tests). The table is current status, not a future plan.
| Module | Status | What it does |
|---|---|---|
| 1. stig-prep | ✅ shipped | Parse any DISA STIG (XCCDF) into engineer-friendly checklists (Markdown / JSON / CSV) with optional AI triage & plain-English explanations |
| 2. stig-scan | ✅ shipped (macOS, Linux, Windows) | Run STIG checks against the local system and record pass/fail with an explicit evidence-coverage statement. A normal scan runs only named-approved, content-frozen checks. --include-unreviewed can force an unreviewed run; those results are labeled untrusted |
| 3. stig-assess | ✅ shipped | Interpret scan results and draft POA&M entries (drafts only; a named person approves wording and is the only one who can close an item). Untrusted scan rows stay flagged and are not accreditation evidence |
| 4. stig-harden | ✅ shipped | Generate remediation scripts for findings, with human-review gates (dry-run / show-script / approve-before-apply). --apply-for-real is refused in CI. The local page only writes drafts; it never applies a script |
Everything is STIG-agnostic: the tools parse standard XCCDF, so the same code works for Windows 11, Windows Server, RHEL, Ubuntu, macOS, SQL Server, or any other STIG that DISA publishes.
No technical skill needed. You do not need the command line.
- On this page click Code → Download ZIP. Unzip the folder.
- Open the unzipped folder until you see
STIG Checker.batandstiguiin the same place. (Windows sometimes creates a folder inside a folder.) - Windows: double-click
STIG Checker.bat. Mac: right-clickSTIG Checker.command→ Open → Open. - Windows may say “Windows protected your PC”. Click More info, then Run anyway. Once.
- If a box asks to download Python, click Yes. That is a private copy from python.org (~11 MB on Windows). It is not installed system-wide and needs no administrator password.
- A page opens in your browser. Pick MacBook or Windows PC and follow the page.
Keep the .bat / .command file inside the unzipped folder. Do not drag it to the Desktop by itself.
The same steps are in Double-click this.txt next to those files.
The page is served on your own computer (127.0.0.1). Nothing is uploaded. It never approves a check for you. A normal scan runs only checks you have approved under your name. An explicit override (the local checkbox, or CLI --include-unreviewed) can run unreviewed checks; those results are labeled untrusted and are not a normal approved scan. After a scan it writes a PDF report next to the JSON and Markdown reports. After a scan the page can also draft POA&M entries and candidate fix scripts; those buttons only write files.
A hosted page leads with the fact that the US Department of Defense publishes free STIG checklists, then explains them and helps you check your PC. It includes a Demo — try a simple first scan path (CAT I only, then a one-click select), then the same first steps — pick MacBook or Windows, read the filed plain-English explanations, tick the rules to scan — then downloads an OS-specific scanner zip preconfigured for that selection. The zip is this project’s Python files plus selection.json. It is not a remote scanner. Hosting caveats (CORS, official zip fetch) sit under Technical notes, not in the hero.
A worked example (docs/demo.html) shows the same path as an article — pick OS, select CAT I, download, approve, read the risk story — with labeled sample data, so a new reader does not have to click through a real scan. Longer background essays live on the personal site, hsaxena.com, not on this product Pages site.
Hosted (this site): lists the validated STIG, shows explanations from annotations/, builds a download. It does not run host checks and does not need an API key. A static Evidence and transparency page lists what a reviewer can verify from public files (licence, approval gate, platforms, how to reproduce a CAT I scan) and what this project does not claim. An optional Used this? page is an invitation only — it does not invent testimonials. Reviews are a separate moderated form: they appear only after a person adds them to docs/data/reviews.json. Contact on every Pages footer is 1992.hsaxena@gmail.com.
Local (the download): stigui on 127.0.0.1, human approval, content-frozen digests, the read-only allowlist, then a PDF on the machine that was scanned.
Expected URL after Pages is on:
https://stig.hsaxena.com
The public site footer lists one email: 1992.hsaxena@gmail.com. Optional personal-site links: hsaxena.com and hsaxena.com/writing.
docs/reviews.html posts to FormSubmit (https://formsubmit.co/1992.hsaxena@gmail.com) with subject STIG site review submission. Required fields: title, full name, email, review text. Organisation or role is optional. Email is for approval only and is never rendered.
The public reviews and thank-you pages do not name docs/data/reviews.json and do not ask visitors to activate FormSubmit. Visitors see that the form emails the maintainer, that reviews appear only after manual approval, and that emails are never published.
Activate FormSubmit (first use, maintainer inbox only). This is a one-time setup of the site owner's inbox, not something submitters do. Submit the form once from the live site. FormSubmit emails 1992.hsaxena@gmail.com an activation link. Click it. Until then, later submissions stay queued and the thank-you page (docs/review-thanks.html) may not appear.
Approve a review. Do not paste the submitter’s email into the public file.
- Read the FormSubmit email. Keep the address off the site.
- Add an object to
docs/data/reviews.json→reviewswithtitle,name, optionalorganization, andtextonly. Noemailfield.
{
"title": "Short headline from the form",
"name": "Full Name",
"organization": "Optional org or role",
"text": "The approved review text."
}- Merge that change. The reviews page loads that JSON and shows title, name (+ optional organisation), and text. An empty list is correct until someone is approved.
The Used this? page stays as the optional public-note / GitHub-issue path. Do not remove it.
The start page Site activity block shows one number, labeled Site visitors: people who opened this website, per GoatCounter’s public counter. That is not DoD adoption, and not a count of people who downloaded or ran the scanner. The public widget does not separate raw pageviews from unique visitors.
Set the site code in one place: docs/site-config.js → goatcounterCode. The committed code is stig-hsaxena (https://stig-hsaxena.goatcounter.com). docs/site-chrome.js then:
- Injects
https://gc.zgo.at/count.jswithdata-goatcounter="https://stig-hsaxena.goatcounter.com/count"on every page that includes the two scripts. - Fills that one Site visitors figure from CORS-safe
https://stig-hsaxena.goatcounter.com/counter/TOTAL.jsonwhencountis greater than 0. If that is 0 or missing, the page sums the public per-path JSON counters for/,/index.html,/reviews.html,/demo.html,/evidence.html,/used.html, and/review-thanks.html(HTTP 404 counts as 0 and does not abort the sum).TOTAL.html#gcvc-viewsis last: browsers often cannot read it (no CORS). If nothing works, the number stays—. Nothing is invented.
In GoatCounter settings, enable Allow adding visitor counts on your website (defaults to off). Making the dashboard public is optional; the JSON counter needs that visitor-counter flag.
GitHub Pages is static files. Browsers block that host (CORS), and many networks block it too. The site therefore ships the catalogue, the pinned release / rule-count (and SHA-256 when one is recorded), and the filed explanations this project validated — the same data stigprep parse --explain attaches. The local program still downloads the official zip when you ask it to, and refuses a file that does not match. If the host is blocked, use public.cyber.mil/stigs/downloads.
Free hosting. Either:
- Branch folder (fewest clicks). Repository Settings → Pages → Build and deployment → Source: Deploy from a branch. Branch
main, folder/docs. The committeddocs/tree is enough. - GitHub Actions. Settings → Pages → Source: GitHub Actions.
.github/workflows/pages.ymlregeneratesdocs/data/from the catalogue and deploys. Same URL.
Refresh the shipped JSON after changing annotations or check packs:
python3 packaging/build_site.py --out docsBuild a configured scanner without the website:
python3 packaging/build_scanpack.py \
--platform macos --pack checkpacks/macos-26-v1r3.json \
--id APPL-26-000054 --id APPL-26-002038 \
-o dist/STIG-Scanner-macOSgit clone https://github.com/himanshusaxenagithub/stig-ai-pipeline.git
cd stig-ai-pipeline
python3 -m stigui --appOn Windows use py -m stigui --app or python -m stigui --app.
# Get the official STIG for your OS, then build a checklist
./scripts/download_stig.sh
python3 -m stigprep parse U_Apple_macOS_15_V1R7_STIG.zip
# Or try the bundled 6-rule sample
python3 -m stigprep parse samples/sample_macos15_stig.xml
python3 -m stigprep fetch --list
python3 -m stigprep fetch windows-11Outputs land in ./out/ (*_checklist.md, *_checklist.json, *_checklist.csv).
What fetch returns is checked before it is used: against a pinned SHA-256 where one is recorded, and always against the rule count this project validated. If the network blocks dl.dod.cyber.mil, it prints the page to use instead rather than failing with a stack trace.
Every rule can carry a plain-English summary, a triage bucket (quick-win,
config-profile, needs-judgment, risky-change), an automation flag and a
one-line caution. No account, no key, no external service.
Already in the repository. annotations/ ships complete explanation
sets. Parse the matching STIG with --explain and they are attached:
python3 -m stigprep parse U_MS_Windows_11_V2R9_STIG.zip --explain
# explanations: all 257 rules annotated from filed sets
Two scoped totals, not one ambiguous number:
- Explained (seven STIGs): 1,440 rules — Microsoft Windows 11 V2R9 (257), Windows Server 2019 V3R8 (282), Red Hat Enterprise Linux 9 V2R9 (445), Ubuntu 24.04 LTS V1R6 (194), Apple macOS 26 V1R3 (160), SQL Server 2022 Instance V1R4 (79) and Database V1R3 (23).
- Check packs (five packaged platforms): 1,338 rules — the five OS guides above (257 + 282 + 445 + 194 + 160). SQL Server is explained but is not shipped as a check pack.
Produce them for any other STIG. The stig-explain skill
(skills/stig-explain/) lets the AI assistant you already have write the
four fields, ten rules at a time. A helper script hands it the next batch,
validates every answer — rejecting the batch if a rating is invalid, a field
is missing, or a command appears that is not in the rule's own text — and
files the results into the checklist and into annotations/. That is how the
Windows 11 set was produced. Commit the file and every later user gets it.
stig-prep tells you what the STIG asks. stig-scan tells you where the
machine actually stands — without ever letting a language model decide what
runs on your system.
DISA's check text is prose written for a human auditor, so something has to turn it into executable logic. The design rule here is that AI is allowed in the authoring loop and never in the execution loop:
STIG check text ──author──▶ candidate check ──human review──▶ frozen check ──scan──▶ evidence
(offline) (UNREVIEWED) (you) (digest)
Approving a check records a SHA-256 digest over exactly the fields that determine what executes — the command, the comparator, the expected value. If any of them is edited afterwards, the digest no longer matches and the check is reported as DRIFTED and refused. An approval always refers to specific bytes a human actually read.
The core is platform-neutral; what differs lives in stigscan/platforms/,
one module per operating system: the read-only allowlist, the forbidden
mutating forms, the shell, and how the extractor reads DISA's check text.
| Profile | Scan | Extractor | Packs shipped |
|---|---|---|
| macos | ✅ | ✅ shell snippet + acceptance sentence | macos-26-v1r3 (160 rules, 153 reducible) |
| linux | ✅ | ✅ prompt lines + mapped sentence shapes | ubuntu-24.04-v1r6 (194, 72 reducible), rhel-9-v2r9 (445, 156 reducible) |
| windows | ✅ PowerShell | ✅ registry / auditpol / secedit / quoted cmdlet shapes | windows-11-v2r9 (257, 154 reducible), windows-server-2019-v3r8 (282, 160 reducible) |
A pack records its platform; scan will not run a pack on a host of a
different platform. Every shipped pack is entirely unreviewed.
# 1. Derive candidate checks from the module 1 checklist (nothing is approved yet)
python3 -m stigprep parse U_Apple_macOS_26_V1R3_STIG_Manual-xccdf.xml --format json
python3 -m stigscan author out/*_checklist.json -o checkpacks/macos-26-v1r3.json
# 2. Read exactly what would execute, highest severity first
python3 -m stigscan review checkpacks/macos-26-v1r3.json --severity high --show
# 3. Freeze the ones you accept, under your own name
python3 -m stigscan approve checkpacks/macos-26-v1r3.json \
--by "Your Name" --id APPL-26-005001 --id APPL-26-002064
# 4. Audit the pack at any time (drift, safety objections, review coverage)
python3 -m stigscan verify checkpacks/macos-26-v1r3.json
# 5. Scan. Only approved, unmodified checks run.
# Writes JSON, Markdown and a PDF into out/.
python3 -m stigscan scan checkpacks/macos-26-v1r3.json -o out/Every check passes a static safety gate before execution, enforced in the
orchestrator so it applies no matter where the output comes from. Two
independent rules must both hold: every executable named in the command is
on a read-only allowlist, and no forbidden construct appears — mutating
subcommands (defaults write, csrutil disable, launchctl unload), file
redirection, network clients, privilege escalation, or destructive verbs.
A check that fails the gate is reported as ERROR, never as pass or fail.
Checks that embed sudo are rewritten to declare requires_root instead,
so the scanner tells you up front which checks need privilege rather than
prompting for a password halfway through a run.
An automated compliance report is only as honest as its coverage statement, so every scan reports one:
11 of 13 rules (84.6%) were actually evaluated on this host. The remaining 2 produced no compliance evidence and must not be counted as either compliant or non-compliant.
Outcomes are pass, fail, error, manual and skipped, and the three
that are not pass/fail are never folded into "fail". Every skip is reported
with its reason. Results from checks forced with --include-unreviewed are
flagged untrusted in both the JSON and the Markdown.
# Capture a scan as a durable artifact
python3 -m stigscan scan checkpacks/macos-26-v1r3.json --record evidence/2026-08-20/
# Replay it anywhere — including CI on a platform that cannot run macOS commands
python3 -m stigscan scan checkpacks/macos-26-v1r3.json --fixtures evidence/2026-08-20/Recording turns a scan into evidence that can be re-examined months later instead of a claim about something that happened once on somebody's laptop.
Authored from the official Apple macOS 26 (Tahoe) V1R3 STIG, 160 rules:
| count | |
|---|---|
| Reducible to a command | 153 |
| No machine-readable acceptance criterion (GUI or interview) | 7 |
| High extractor confidence | 64 |
| Medium — multi-line snippet, confirm it is self-contained | 81 |
| Low — safety gate objection or ambiguity, needs a decision | 8 |
The pack ships with every entry unreviewed. That is not an oversight; an approval is worthless if it was not made by the person accountable for the system.
skills/stig-explain/ lets any AI assistant that supports skills produce the
summary / triage / automation / caution fields, ten rules
at a time. A helper script hands the assistant the next batch
and validates and files the answers. Results land in the checklist JSON and in
annotations/<stig>.ai-cache.json, which --explain attaches — so
annotations committed to this repository are available to every user, with or
without an assistant. annotations/ carries complete sets for seven STIGs
(1,440 explained rules). Five of those guides ship as check packs (1,338
scan candidates). SQL Server 2022 is explained only.
skills/stig-scan-macos/ and skills/stig-scan-linux/ let an assistant drive
Module 2 end to end — author, verify, walk the user through review, then scan
and explain the report. The assistant never approves a check and never runs
an unreviewed one; those lines are in the skill text and enforced by the
scanner. skills/stig-scan-windows/ drives it on Windows through PowerShell.
stig-scan tells you where the machine stands. stig-assess turns the
failures into Plan of Action and Milestones drafts a person can accept,
reject, or close — without ever closing a finding on its own.
The same design rule as the scanner applies to status: AI is allowed in the authoring loop and never in the closure loop.
scan JSON ──draft──▶ POA&M wording ──human approve──▶ open item ──human close──▶ closed
(offline) (UNREVIEWED) (you; digest) (you; note)
Approving a draft freezes a SHA-256 digest over the wording a person
read and moves the item from draft to open. It does not mark the
finding complete. Closing is a separate command that requires a name, a
kind (remediated, risk_accepted, or not_applicable), and a note.
A later passing scan does not close anything.
# 1. Draft from a scan. Default: failures only. Passes are refused.
python3 -m stigassess draft out/macos-26-v1r3_scan.json -o out/macos-26-v1r3-poam.json \
--checklist out/*_checklist.json
# 2. Read the drafts, CAT I first
python3 -m stigassess review out/macos-26-v1r3-poam.json --severity high --show
# 3. Accept the wording under your own name (item becomes open, not closed)
python3 -m stigassess approve out/macos-26-v1r3-poam.json \
--by "Your Name" --id APPL-26-005001
# 4. Later, you close it — the only path that can
python3 -m stigassess close out/macos-26-v1r3-poam.json \
--by "Your Name" --id APPL-26-005001 \
--kind remediated --note "SIP enabled in Recovery; re-scan pass"
python3 -m stigassess render out/macos-26-v1r3-poam.json -o out/
python3 -m stigassess verify out/macos-26-v1r3-poam.json--include fail,error,manual also drafts evaluation gaps and GUI /
interview items. Those rows are labelled as gaps, not findings, and must
not be counted as pass or fail. Untrusted scan results (checks forced
with --include-unreviewed) stay in the pack and are flagged; they are
not accreditation evidence.
stig-assess writes what to record. stig-harden writes what a person
might run to fix a finding — and then stops. The same freeze as the
scanner applies to execution:
scan JSON ──author──▶ candidate script ──human review──▶ frozen script ──dry-run──▶ (optional apply)
(offline) (UNREVIEWED) (you) (digest) (default)
Known check shapes invert to a draft: Windows Get-ItemProperty →
Set-ItemProperty, auditpol /get → /set; macOS defaults read →
defaults write; Linux sysctl KEY → sysctl -w. High-risk forms
(SIP, FileVault, secedit /configure) stay manual. Destructive verbs
and network clients are refused. Passing results are not drafted.
python3 -m stigharden author out/macos-26-v1r3_scan.json -o out/macos-26-v1r3-harden.json \
--checklist out/*_checklist.json --checkpack checkpacks/macos-26-v1r3.json
python3 -m stigharden review out/macos-26-v1r3-harden.json --show
python3 -m stigharden approve out/macos-26-v1r3-harden.json \
--by "Your Name" --id APPL-26-002064
# Default is dry-run. --apply-for-real is refused when CI=true.
python3 -m stigharden apply out/macos-26-v1r3-harden.json \
--by "Your Name" --id APPL-26-002064 --i-have-reviewed
python3 -m stigharden script out/macos-26-v1r3-harden.json -o out/remediations/
python3 -m stigharden verify out/macos-26-v1r3-harden.jsonThe local page offers Draft POA&M entries and Draft fixes after a scan. Those buttons only write drafts. They never close a finding and never apply a script.
skills/stig-assess/ lets an assistant draft and refine POA&M wording
from a scan report, ten items at a time. A helper script hands it the
next batch and validates the answers — rejecting the whole batch if it
tries to close an item, set a review state, or invent a command that is
not already on the finding. The assistant never runs approve or
close; those lines are in the skill text and enforced by the module.
skills/stig-harden/ lets an assistant invert failed checks into
scripts a person can read. The helper rejects any batch that would mark
a script applied, file a high-risk form as executable, or fail the
safety gate. The assistant never runs approve or apply.
skills/stig-to-tracker/ packages this workflow as a reusable AI skill.
Installed into an AI assistant that supports skills (such as Claude), it
turns a single instruction like "parse the STIG file" into the full
chain: locate the STIG, run stig-prep, validate the rule count, build a
formatted Excel tracker, and flag anomalies for human review.
The tracker builder also works standalone:
pip install openpyxl # the only extra dependency, used just for this step
python3 -m stigprep parse U_Apple_macOS_15_V1R7_STIG.zip --format json
python3 skills/stig-to-tracker/scripts/make_tracker.py \
out/*_checklist.json macos_stig_tracker.xlsxThe workbook has three sheets: Summary (counts by severity), Tracker (one row per rule, CAT I first, status dropdowns, owner/date/notes columns), and Details (full check and fix text per rule).
python3 -m stigprep parse <stig.zip|xccdf.xml>
-o, --out DIR output directory (default: ./out)
--format LIST md,json,csv (default: all)
--explain attach plain-English explanations filed in annotations/
python3 -m unittest discover -s testssamples/sample_macos15_stig.xmlis a 6-rule illustrative excerpt for testing the parser — its text is abbreviated and its severities are illustrative. Never harden a system from the sample; use the official package from public.cyber.mil.- This project is built entirely on publicly released DISA STIG content.
MIT