jacobyoung-sec/xsiam-dac
GitHub: jacobyoung-sec/xsiam-dac
Stars: 0 | Forks: 0
# XSIAM Detection as Code — PoC
A pipeline for managing XSIAM correlation rules the same way you manage application code: version controlled, peer reviewed, and automatically deployed. The goal is to prove out the workflow on GitHub before porting it to GitLab for production use.
## The Idea
Detection engineers write rules either as Sigma (vendor-agnostic) or directly as XQL (XSIAM-native). Either way, the rule goes through the same pipeline: lint → validate → convert → review → deploy. No manual API calls, no clicking around in the UI, no rules that only live in someone's head.
Sigma YAML ──► CI converts to XQL ──► JSON template committed to branch
│
▼
XQL JSON ──────────────────────────► Engineer fills in PR ──► merge ──► auto-deploy
Rules are deployed to XSIAM via the `/public_api/v1/correlations/insert` endpoint. The endpoint is an upsert — same call for create and update, distinguished by whether `rule_id` is present in the request body. The deploy script resolves that `rule_id` by querying XSIAM for an existing rule with the same `name`; **XSIAM itself is the source of truth for "what is deployed"** — the repo only stores detection definitions, not deploy state.
## Repo Layout
rules/
sigma/ # Sigma rules (.yml) — write here if you're using Sigma
xql/ # Deployment JSONs (.json) — generated or written directly
schemas/
detection_schema.json # JSON Schema for Sigma rule validation
tools/
sigma_convert.py # Converts Sigma → XQL, writes deployment JSON template
validate_rule.py # Validates Sigma rules against schema + MITRE tags
validate_deployment.py # Validates deployment JSON fields before pushing to XSIAM
validate_xql.py # Static analysis on XQL queries
deploy_rule.py # POSTs/PATCHes rules to XSIAM API
bump_version.py # Auto-increments _version in changed deployment JSONs
state_manager.py # CLI for viewing deployment state across all rules
fake_xsiam_server.py # Local Flask server that mimics the XSIAM correlation rules API
validation/
allowed_datasets.yml # Allowlist for dataset/parser dependencies
mitre_attack.json # MITRE ATT&CK tactic/technique lookup table
requirements.txt # Single source of Python dependencies (pinned)
## Pipeline Stages
The GitHub Actions pipeline runs on every push and PR.
| Stage | What it does |
|---|---|
| 1 — Lint | yamllint on Sigma rules, JSON-schema validation of Sigma rules |
| 2 — Sigma → XQL + Bump | Converts new Sigma rules, bumps `_version` on changed JSONs, single combined commit; posts an XQL preview comment on PRs |
| 3 — Deployment Validation | Validates deployment JSONs against the XSIAM API constraints — lenient on branch pushes (warnings), strict on PRs to main (errors) |
| 4 — Static XQL Analysis | Basic structural checks on the XQL query |
| 5 — Unit Tests | Runs anything in `tests/unit/` |
| 6 — Security | Scans for hardcoded internal IPs |
| 7 — Deploy DEV | Deploys to XSIAM on merge to main |
| 8 — State Report | Queries XSIAM for live rules and prints a deploy-status summary |
## Adding a Rule
### Sigma rule
1. Branch off main
git checkout -b detection/my-rule-name
2. Drop a `.yml` file into `rules/sigma//`. Standard Sigma format — no XSIAM-specific fields needed.
3. Push and open a PR. CI will convert the rule to XQL and commit a JSON template to your branch automatically.
4. Pull the branch, open `rules/xql//my_rule.json`, and fill in the blanks:
- `alert_category` — e.g. `LATERAL_MOVEMENT`, `DISCOVERY`, `PERSISTENCE`
- `search_window` — e.g. `1 hours`
- `execution_mode` — `REAL_TIME` or `SCHEDULED`
- `simple_schedule` / `crontab` — if scheduled
- `description`, `alert_description`
5. Push, get a review, merge. On merge to main the rule deploys to DEV automatically.
### Native XQL rule
## Deployment JSON Fields
Every deployment JSON has two kinds of fields:
**`_`-prefixed fields** are internal to the pipeline and get stripped before anything is sent to XSIAM:
| Field | Purpose |
|---|---|
| `_version` | Auto-incremented integer when the public content of the file changes |
| `_note` | Reminder not to manually edit `_version` |
| `_content_hash` | Fingerprint of public fields used by `bump_version.py` to decide whether to bump |
**Everything else** maps directly to the XSIAM correlation rules API. Key fields:
| Field | Values |
|---|---|
| `severity` | `SEV_010_INFO`, `SEV_020_LOW`, `SEV_030_MEDIUM`, `SEV_040_HIGH`, `SEV_050_CRITICAL` |
| `execution_mode` | `REAL_TIME`, `SCHEDULED` |
| `alert_category` | `LATERAL_MOVEMENT`, `DISCOVERY`, `PERSISTENCE`, `EXECUTION`, `EVASION`, `OTHER`, `TAMPERING`, `FILE_TYPE_OBFUSCATION`, `PRIVILEGE_ESCALATION`, `CREDENTIAL_ACCESS`, `COLLECTION`, `EXFILTRATION`, `INFILTRATION`, `DROPPER`, `FILE_PRIVILEGE_MANIPULATION`, `RECONNAISSANCE` |
| `is_enabled` | boolean — `true` or `false` |
| `suppression_enabled` | boolean — `true` or `false` |
| `drilldown_query_timeframe` | `ALERT`, `QUERY` |
| `mapping_strategy` | `AUTO`, `CUSTOM` |
## Running Locally
**One-time setup**
pip install -r requirements.txt
**Validate Sigma rules**
python tools/validate_rule.py
**Convert a single Sigma rule**
python tools/sigma_convert.py --file rules/sigma/windows/my_rule.yml
**Start the fake XSIAM server**
python tools/fake_xsiam_server.py
# Listening on http://localhost:5000
**Deploy to the fake server**
XSIAM_URL=http://localhost:5000 python tools/deploy_rule.py --env DEV
**Check deployment state (queries XSIAM live)**
python tools/state_manager.py list
## What's Not Here Yet
This is a PoC — some things are intentionally left for the production port:
- **Real XSIAM auth** — set `XSIAM_AUTH` (the API key) and `XSIAM_AUTH_ID` (the `x-xdr-auth-id` value) as GitHub secrets / env vars; `deploy_rule.py` and `state_manager.py` attach them as headers when present
- **Environment promotion** — DEV → testing → security is manual right now; the promotion gates need to be built out
- **Approval gates** — No enforced sign-off before promotion (would use protected environments + MR approvals in GitLab)
- **Rollback** — No automated way to disable a rule that starts firing badly after deployment
- **Notifications** — No alerting on pipeline failure
## Porting to GitLab
The Python tooling has no GitHub-specific dependencies and ports as-is. The pipeline needs to be rewritten as `.gitlab-ci.yml` — the stage structure stays the same, syntax differs. The main thing to sort out is CI push-back (the auto-commit steps): GitLab's `CI_JOB_TOKEN` is read-only by default so you'll need a project access token or deploy key.