idira-audit-clj — IAM/PAM Estate Audit CLI
What it is
Manual privilege auditing and risky IAM drift get replaced with
deterministic, version-controlled rule evaluation:
query a CyberArk/Idira-style REST API (SCIM users/groups, tokens, MFA
policy) and emit deterministic EDN/JSON audit reports —
orphaned privileged accounts, dormant privilege, unvaulted accounts,
missing MFA, stale tokens. The same rules run against a
shape-compatible mock API built in, so the 5 fixture
findings reproduce exactly anywhere with only bb.
Scope, stated plainly: this audits a shape-compatible mock
API or any SCIM/OAuth2 endpoint you point it at. There is
no CyberArk integration and no affiliation with
CyberArk — “Idira-style” means the endpoint shapes, not a
partnership. A standalone GraalVM single-binary build was evaluated and
deferred (see the README): bb itself is
already a GraalVM binary with millisecond startup, so a separate native
binary buys nothing today.
The rules (the executable contract)
| # | Rule | Target |
|---|---|---|
| 1 | orphaned-privileged (high) |
Privileged account with no human owner |
| 2 | inactive-privileged (medium) |
Privileged, dormant 120 days |
| 3 | unvaulted-privileged (high) |
Privileged, not vault-managed |
| 4 | missing-mfa (medium) |
No MFA enrolled |
| 5 | stale-token (medium) |
Token older than 180-day TTL |
| 6 | never-logged-in-privileged (high) |
Privileged, no recorded login — synthetic estate only (not in the 5-finding mock fixture) |
Fail-closed on gaps: never-logged-in privileged
accounts and tokens without :created are findings, never
silent passes. Rules are pure (no I/O) — the caller passes the
clock.
5-minute quickstart (mock — deterministic, exactly 5 findings)
git clone https://github.com/nurazhardotcom/idira-audit-clj
cd idira-audit-clj
bb test # 14 tests / 45 assertions
bb audit-demo # end-to-end audit of the built-in mock API
bb -m idira-audit.main audit --mock --format json
Start a stub API and audit a sibling service via the bridge:
bb -m idira-audit.main mock-server --port 8899
bb -m idira-audit.main audit --idszc ../idszc --format json
bb check-native # GraalVM readiness report
Live API
bb -m idira-audit.main audit --base-url https://idira.example \
--token-url https://idira.example/oauth2/token \
--client-id LAB --client-secret '...' --format json
# or: --api-token '...' instead of the client-credentials trio
File map
src/idira_audit/rules.clj— pure rule engine:(audit-users estate now)→ findings.src/idira_audit/policy.clj— MFA / assurance / device-posture evaluation.src/idira_audit/auth.clj— OAuth2 client-credentials or static API token.src/idira_audit/scim.clj— fetch + normalize/Users /Groups /Tokens /Policies.src/idira_audit/mock.clj— stub API on JDKServerSocketonly; fixed clock and fixture estate with exactly 5 findings.src/idira_audit/idszc.clj— bridge auditing the siblingidszcservice via/inventory.src/idira_audit/main.clj— CLI.
Labs
When it gets messy — a hybrid estate with orphans, dormant privilege, and MFA gaps meets the auditor:
Lab 1 — reproduce the 5. Run
bb audit-demo; map each finding to its rule row above.
Lab 2 — break a rule safely. Flip one mock fixture field (e.g. give the orphan an owner), re-run, confirm the finding disappears and tests catch the drift.
Lab 3 — JSON to ticket. Emit
--format json, pipe one finding into your ticketing format
— deterministic fields, no parsing surprises.
CI
GitHub Actions runs bb test + bb audit-demo
on every push to main and every PR — both steps exit
non-zero on failure, so the deploy job never runs on a broken audit.