feat: add --siem flag to security-audit report for NDJSON export#1876
feat: add --siem flag to security-audit report for NDJSON export#1876jlima8900 wants to merge 1 commit intoKeeper-Security:releasefrom
Conversation
0460eea to
465e68a
Compare
Adds SIEM-ready output to security-audit report using NDJSON format (one JSON event per line). Compatible with Splunk HEC, Elastic Filebeat, Datadog log pipelines, and any NDJSON/JSON Lines consumer. Usage: security-audit report --siem --output posture.ndjson security-audit report --siem --breachwatch --output breach.ndjson Design decisions: - NDJSON not wrapped JSON — supports streaming ingestion at scale - --siem flag not --format siem — avoids modifying shared parser - Risk factors from data, no hardcoded severity thresholds - .ndjson extension for file output - twoFactorChannel values verified: 'Off' or 'On' (from security_audit.py:356) - params.server verified: exists as property on KeeperParams Existing formats (table, csv, json, pdf) unchanged.
465e68a to
d46b168
Compare
| report_title = f'Security Audit Report{" (BreachWatch)" if show_breachwatch else ""}' | ||
|
|
||
| # SIEM-ready NDJSON output | ||
| if kwargs.get('siem'): |
There was a problem hiding this comment.
This gets silently ignored when --record-details is set, because the method returns from the record-detail branch before it ever reaches the SIEM branch. If we want to support that combination, it needs handling there; otherwise we should reject the flag combo explicitly instead of accepting it and returning the normal record-detail report.
| report = '\n'.join(ndjson_lines) + '\n' | ||
|
|
||
| if filename: | ||
| if not os.path.splitext(filename)[1]: |
There was a problem hiding this comment.
This silently bypasses the shared --format / report-output path. Once --siem is set, fmt no longer governs behavior, and --output is honored even though the shared parser help says output is ignored for table mode. The main issue here is the lack of validation or explicit contract for how --siem interacts with --format and the normal report-output path. I would either validate the combination and fail closed, or make the override explicit in the CLI contract.
| } | ||
| ndjson_lines.append(json.dumps(event, default=str)) | ||
|
|
||
| report = '\n'.join(ndjson_lines) + '\n' |
There was a problem hiding this comment.
For the empty case this produces a single blank line, not zero NDJSON records, because "\n".join([]) + "\n" is just "\n". That is not actually "one JSON object per line" output, and some ingest pipelines will treat the blank line as a parse error. We should special-case the empty report to emit an empty string/file instead.
Summary
Adds SIEM-ready output to
security-audit reportusing NDJSON format (one JSON event per line). The existingaudit-logcommand has SIEM export (Splunk, syslog, Sumo, Azure) for event streaming, butsecurity-audit report(posture snapshots) has no SIEM output.Usage
Output format (NDJSON — one line per user)
{"event_type":"keeper.security_audit","timestamp":"2026-03-14T22:30:00+00:00","source":"keepersecurity.eu","user":{"email":"user@company.com","securityScore":52,"weak":8,"reused":6,"twoFactorChannel":"none"},"security_score":52,"risk_factors":["weak_passwords","reused_passwords","no_2fa"]}Design decisions
jq -c, Filebeat, Splunk HEC, Datadog logs.--siemflag not--format siem— avoids modifying the sharedreport_output_parserused by all commands. Clean addition, no side effects.risk_factorsandsecurity_score. SIEM correlation rules should define what's critical for each organization, not Commander..ndjsonextension — standard for newline-delimited JSON files.No breaking changes
--siemis additive — only affects output when explicitly usedaudit-logSIEM targets unaffectedTest plan
--siemproduces valid NDJSON (one JSON object per line)jq--breachwatch --siemincludes at_risk/passed/ignored--outputwrites to file with.ndjsonextension--output, prints to stdout--format jsonoutput unchanged