opseclint 0.1.0

Detection-coverage analyzer for Linux/auditd, Windows/Sysmon, and macOS/Endpoint Security: resolve shell/command actions to ATT&CK techniques, the telemetry they emit, and the detections that would fire.
# opseclint

[![CI](https://github.com/Gerrrt/opseclint/actions/workflows/ci.yml/badge.svg)](https://github.com/Gerrrt/opseclint/actions/workflows/ci.yml)

**A detection-coverage analyzer for the command line.** Point it at a command,
a script, or a post-exploitation playbook and it statically resolves each action
to the [MITRE ATT&CK](https://attack.mitre.org/) technique(s) it implements, the
host telemetry it emits, and the detections that would fire — each with a
detectability score.

It answers one question: **"what would a defender see?"**

![opseclint demo](docs/demo.svg)

## Who it's for

- **Detection engineers** validating coverage — "if an operator ran this, would
  my ruleset catch it, and with what telemetry?"
- **Purple teams** mapping an engagement's actions to expected detections before
  and after a test.
- **Red teams** operating under authorization who need to understand and report
  the telemetry footprint of a playbook.

### Scope

opseclint describes **detectability** — the defensive signal an action
generates. It is not an evasion tool: it does not recommend "quieter"
alternatives or ways to defeat a detection. Absence of a finding means only that
nothing in the knowledge base matched — it is never a claim that an action is
stealthy.

## Install

```bash
cargo install --path .
# or, from a checkout:
cargo build --release   # -> target/release/opseclint
```

## Usage

```bash
opseclint script.sh                 # analyze a file (Linux/auditd by default)
opseclint -c 'sudo cat /etc/shadow' # analyze a single command
cat playbook.sh | opseclint         # read from stdin
opseclint app.ps1 --platform windows-sysmon   # analyze against Windows/Sysmon

opseclint script.sh --min 50        # only show findings >= detectability 50
opseclint script.sh --json          # machine-readable output
opseclint script.sh --sarif         # SARIF 2.1.0 (GitHub code scanning)
opseclint script.sh --sigma ./sigma # enrich with a real SigmaHQ checkout
opseclint script.sh --ci --threshold 70   # exit 1 if loudest action >= 70
```

### Real Sigma rules

By default, detection references in the seed KB are *representative*. Point
`--sigma` at a checkout of [SigmaHQ/sigma](https://github.com/SigmaHQ/sigma)
(or any directory of Sigma YAML) and opseclint indexes every rule by its ATT&CK
technique tag, then replaces each finding's references with the **genuine rule
titles and UUIDs** that match — Linux-relevant rules only.

```bash
git clone --depth 1 https://github.com/SigmaHQ/sigma
opseclint examples/recon.sh --sigma sigma/rules
# detection  Sigma: Access To Sudoers File (2c9d1141-... ) (high confidence)
```

The ruleset is read at runtime and never bundled, so the binary stays
self-contained. The parsed index is cached to disk (keyed by a fingerprint of
the ruleset directory), so repeat runs against a large checkout skip re-parsing
— the report notes `[cached]` on a hit. The cache lives in the system temp dir
(override with `OPSECLINT_CACHE_DIR`) and invalidates automatically when the
ruleset changes; `--no-sigma-cache` bypasses it.

### Platforms

Select the host telemetry model with `--platform` (default `linux-auditd`):

| Platform          | Telemetry model                                   |
|-------------------|---------------------------------------------------|
| `linux-auditd`    | Linux with auditd / EDR syscall events            |
| `windows-sysmon`  | Windows with Sysmon (Event IDs) / Security log    |
| `macos-es`        | macOS with Endpoint Security (ESF) / unified log  |

Each platform has its own embedded knowledge base, so `whoami` resolves to Linux
`execve()` telemetry, a Windows Sysmon EID 1, or a macOS ESF `NOTIFY_EXEC`
depending on the target. Windows program names are normalized
(`C:\…\certutil.exe` → `certutil`). When combined with `--sigma`, rules are
filtered to the platform's `logsource.product`.

### GitHub code scanning

`--sarif` emits SARIF 2.1.0, so findings can surface in a repo's **Security →
Code scanning** tab. Each finding maps to a rule (tagged with its ATT&CK
technique and a `security-severity` derived from the detectability score) and is
anchored to the line of the analyzed file. See [`.github/workflows/ci.yml`](.github/workflows/ci.yml)
for a job that runs opseclint and uploads the results.

### Use as a GitHub Action

opseclint ships a composite action ([`action.yml`](action.yml)) that downloads
a released binary and analyzes a path in CI (Linux runners):

```yaml
- uses: Gerrrt/opseclint@v0.1.0
  with:
    path: examples/
    platform: linux-auditd     # or windows-sysmon | macos-es
    fail-threshold: "75"       # optional: fail the job on a loud action
    sarif-file: opseclint.sarif # optional: emit SARIF...

- uses: github/codeql-action/upload-sarif@v3   # ...then upload it
  with:
    sarif_file: opseclint.sarif
```

Inputs: `path` (required), `platform`, `version` (default `latest`),
`sarif-file`, `fail-threshold`, and `args` for anything else.

### CI gating

`--ci` makes opseclint a gate: it exits non-zero when the loudest modeled action
meets or exceeds `--threshold` (default 50), so a team can fail a pipeline on
tradecraft that exceeds an agreed noise budget.

```yaml
# .github/workflows/opsec.yml (example)
- run: opseclint playbooks/ --ci --threshold 75
```

### Example playbooks

The [`examples/`](examples/) directory has illustrative (benign-to-run)
playbooks to try it against:

```bash
opseclint examples/recon.sh           # post-compromise recon (Linux)
opseclint examples/persistence.sh     # accounts, cron, systemd, ld.so.preload, ...
opseclint examples/defense-evasion.sh # SELinux/firewall/auditd off, log & history wiping
opseclint examples/windows-postex.ps1 --platform windows-sysmon  # Windows LOLBins, cred access
opseclint examples/macos-postex.sh    --platform macos-es        # keychain, Gatekeeper, launchd
```

## Detectability score

A 0–100 estimate of how strongly an action surfaces in defensive telemetry
(higher = louder), bucketed as:

| Score  | Severity  |
|--------|-----------|
| 0–24   | LOW       |
| 25–49  | MEDIUM    |
| 50–74  | HIGH      |
| 75–100 | CRITICAL  |

## How it works

1. **Parser** (`parser.rs`) — quote-aware tokenizer that strips comments and
   `VAR=value` assignments, splits a line on control operators (`; | & && ||`),
   unwraps `sudo`/`env`/`nohup`/… and resolves each segment to a program +
   arguments. The raw line is preserved so substring rules still match. A
   preprocessing pass joins line continuations (trailing `\`, `|`, `&&`, `||`),
   resolves commands hidden in `$(...)` / backtick substitutions, and handles
   here-docs — a here-doc body is skipped as data unless it feeds a shell
   interpreter, in which case each body line is analyzed at its real line.
2. **Knowledge base** (`data/knowledge*.json`) — one KB per platform (Linux,
   Windows, macOS); each entry maps a command (or a raw pattern) to ATT&CK
   techniques, the telemetry it emits, representative Sigma-style detections,
   and a detectability score.
3. **Analyzer** (`analyzer.rs`) — matches every action against the KB,
   deduplicates per line, and ranks findings loudest-first.
4. **Report** (`report.rs`) — terminal or JSON output, plus the CI gate.

All KBs are embedded at compile time, so opseclint ships as a single static
binary with no runtime dependencies.

### Knowledge base schema

```json
{
  "id": "shadow-read",
  "raw_contains": "/etc/shadow",
  "description": "Access to /etc/shadow — password hash exposure",
  "techniques": [{ "id": "T1003.008", "name": "OS Credential Dumping: /etc/passwd and /etc/shadow" }],
  "telemetry": ["openat() of /etc/shadow — high-signal auditd file watch"],
  "detections": [{ "source": "Sigma", "rule": "...", "confidence": "high" }],
  "noise": 85
}
```

An entry matches either by `command` (with optional `args_contains` /
`raw_contains` refinements) or by `raw_contains` alone. Adding coverage is a
data change, not a code change.

## Status & roadmap

`v0.1` seeds three platforms: **Linux / auditd** (~60 entries), **Windows /
Sysmon** (~60 entries, including Active Directory tradecraft — Kerberoasting,
DCSync, ticket attacks, BloodHound/PowerView recon, and lateral movement), and
**macOS / Endpoint Security** (~28 entries — keychain, Gatekeeper/SIP, TCC,
launchd persistence, AppleScript). Together they cover the most common
post-exploitation actions across discovery, credential access, execution,
persistence, defense evasion, and container escape. On the roadmap:

- Deepen each KB further and add more EDR-specific telemetry mappings.
- A `--format sarif` upload for Windows/macOS example runs in CI.

**Detection references in the seed KB are representative** of publicly available
Sigma logic and should be validated against your deployed ruleset before you
rely on them.

## License

MIT