Skip to content

About

Turn any DISA STIG into a plain-English, risk-ranked hardening plan — and check a machine against it under a human approval gate. Free, MIT, no API key.

Topics

Resources

Stars

14 stars

Watchers

1 watching

Forks

Repository files navigation

stig-ai-pipeline

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.

Why this exists

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.

Who it is for

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

What you get

  • A week of specialist work in a few minutes. parse turns 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). The stig-explain skill produces more with the AI assistant you already have.
  • A scan you can trust. stig-scan checks 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-assess turns 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-harden drafts a remediation from a known check shape. Approval freezes the script a person read. apply is a dry-run unless --apply-for-real is passed with --by and --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.

Roadmap

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.

Quick start

No technical skill needed. You do not need the command line.

  1. On this page click Code → Download ZIP. Unzip the folder.
  2. Open the unzipped folder until you see STIG Checker.bat and stigui in the same place. (Windows sometimes creates a folder inside a folder.)
  3. Windows: double-click STIG Checker.bat. Mac: right-click STIG Checker.command → Open → Open.
  4. Windows may say “Windows protected your PC”. Click More info, then Run anyway. Once.
  5. 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.
  6. 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.

Public website (GitHub Pages)

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

Contact

The public site footer lists one email: 1992.hsaxena@gmail.com. Optional personal-site links: hsaxena.com and hsaxena.com/writing.

Moderated reviews

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.

  1. Read the FormSubmit email. Keep the address off the site.
  2. Add an object to docs/data/reviews.json → reviews with title, name, optional organization, and text only. No email field.
{
  "title": "Short headline from the form",
  "name": "Full Name",
  "organization": "Optional org or role",
  "text": "The approved review text."
}
  1. 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.

GoatCounter (site activity)

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:

  1. Injects https://gc.zgo.at/count.js with data-goatcounter="https://stig-hsaxena.goatcounter.com/count" on every page that includes the two scripts.
  2. Fills that one Site visitors figure from CORS-safe https://stig-hsaxena.goatcounter.com/counter/TOTAL.json when count is 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-views is 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.

Why the site does not fetch dl.dod.cyber.mil

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.

Turn on GitHub Pages

Free hosting. Either:

  1. Branch folder (fewest clicks). Repository Settings → Pages → Build and deployment → Source: Deploy from a branch. Branch main, folder /docs. The committed docs/ tree is enough.
  2. GitHub Actions. Settings → Pages → Source: GitHub Actions. .github/workflows/pages.yml regenerates docs/data/ from the catalogue and deploys. Same URL.

Refresh the shipped JSON after changing annotations or check packs:

python3 packaging/build_site.py --out docs

Build 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-macOS

Command line

git clone https://github.com/himanshusaxenagithub/stig-ai-pipeline.git
cd stig-ai-pipeline
python3 -m stigui --app

On 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-11

Outputs 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.

Plain-English explanations

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.

Module 2: stig-scan

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.

Platforms

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.

Workflow

# 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/

The scanner will not run arbitrary code

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.

Reports say what did not 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.

Reproducible scans

# 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.

What the macOS 26 pack looks like

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.

AI skill: plain-English explanations

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.

AI skills: scanning

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.

Module 3: stig-assess

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.

Module 4: stig-harden

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.json

The 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.

AI skill: assess a scan

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.

AI skill: draft remediations

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.

AI skill: one-command tracker

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.xlsx

The 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).

Options

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/

Tests

python3 -m unittest discover -s tests

Notes

  • samples/sample_macos15_stig.xml is 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.

License

MIT

About

Turn any DISA STIG into a plain-English, risk-ranked hardening plan — and check a machine against it under a human approval gate. Free, MIT, no API key.

Topics

Resources

Stars

14 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages