Security ยท SIEM detection rules
SIEM detection rules
Copy-paste detection-rule templates so your SOC sees Thoma security events in your existing incident pipeline from day one. Six high-signal alert classes × four formats = 24 ready-to-import snippets. Plus OCSF v1.3.0 webhook output for normalization-aware SIEMs.
What Thoma emits
Three classes of structured security events go out to SIEM-compatible destinations:
- Auth events — successful logins, failed logins, account lockouts, AD-driven user deactivations. Webhook (JSON over HTTPS POST) and/or syslog UDP (CEF or RFC 5424).
- Chain integrity alerts — audit-log chain breaks, electronic-signature chain breaks, vault file integrity breaks. Webhook only (per-channel routable).
- AI quota alerts — LLM token-budget threshold crossings (only relevant if the AI Assistant feature is enabled on the org).
Six high-signal alert classes
The following rules cover the six highest-signal indicators a SOC analyst will care about. Each rule is shown in four formats; pick the one matching your platform.
1. Audit-log chain break
What it means: A row in Thoma's audit_log table has been
modified or deleted post-insertion, or the hash chain between rows has been tampered. CMMC
AU-9 audit-finding-class event. Not a false-positive class — the scanner has zero known
noise sources.
# Splunk SPL
index=thoma sourcetype=thoma_security event=audit_chain_break
| stats count by orgId, verificationId, breakCount, lastRowHash
| where count > 0 # Datadog
service:thoma @event:audit_chain_break @severity:critical # Wazuh ruleset XML
<rule id="100501" level="14">
<decoded_as>json</decoded_as>
<field name="event">audit_chain_break</field>
<description>Thoma audit-log chain break detected (CMMC AU-9)</description>
</rule> # Elastic / Kibana / Sentinel KQL
event: "audit_chain_break" and severity: "critical" 2. Electronic-signature chain break
What it means: A row in Thoma's electronic_signatures table has
been mutated or the chain between e-sig rows is broken. 21 CFR Part 11 / AS9100 / IATF 16949
audit-finding-class event — same severity as audit-log break, different downstream
regulatory pipeline.
# Splunk SPL
index=thoma sourcetype=thoma_security event=esig_chain_break
| stats count by orgId, verificationId, breakCount, lastSignatureHash # Datadog
service:thoma @event:esig_chain_break @severity:critical # Wazuh
<rule id="100502" level="14">
<decoded_as>json</decoded_as>
<field name="event">esig_chain_break</field>
<description>Thoma electronic-signature chain break (21 CFR Part 11)</description>
</rule> # KQL
event: "esig_chain_break" and severity: "critical" 3. Vault file integrity break
What it means: A file in Thoma's document vault no longer matches the
SHA-256 hash captured at upload time. kind:mismatch = on-disk tampering;
kind:missing = file deleted from disk; kind:unreadable =
filesystem-level corruption. All three break the 21 CFR Part 11 §11.10(b) accurate-
copies guarantee.
# Splunk SPL
index=thoma sourcetype=thoma_security event=vault_integrity_break
| spath "breaks{}.kind" output=kinds
| stats values(kinds) as breakKinds, count by orgId, verificationId # Datadog (tampering vs corruption split)
service:thoma @event:vault_integrity_break @breaks.kind:mismatch # Wazuh โ base + severity-elevated tampering variant
<rule id="100503" level="14">
<decoded_as>json</decoded_as>
<field name="event">vault_integrity_break</field>
<description>Thoma vault file integrity break (21 CFR ยง11.10(b))</description>
</rule>
<rule id="100504" level="15">
<if_sid>100503</if_sid>
<field name="breaks.kind">mismatch</field>
<description>Thoma vault file TAMPERED on disk (severity-elevated)</description>
</rule> # KQL
event: "vault_integrity_break" and breaks.kind: "mismatch" 4. Brute-force login (rate-based)
What it means: A single source IP or single account is generating an unusual
rate of auth.login.failure events. Thoma's per-account lockout (default
MAX_FAILED_LOGINS=5) catches the per-account case in-product; this SIEM-side
rule catches distributed brute-force, slow brute-force (paced under the per-account
threshold), and credential stuffing. Tune thresholds for your scale.
# Splunk SPL
index=thoma sourcetype=thoma_security eventType=auth.login.failure
| bin _time span=5m
| stats dc(actor) as distinct_users, count as total_failures by sourceIp, _time
| where total_failures > 20 OR distinct_users > 5 # Datadog (Anomaly Detection monitor)
service:thoma @eventType:auth.login.failure
# Monitor type: Anomaly Detection on @sourceIp, 5m window, 1-week baseline. # Wazuh โ credential-stuffing variant
<rule id="100511" level="10" frequency="20" timeframe="300">
<if_matched_sid>100510</if_matched_sid>
<same_field>sourceIp</same_field>
<description>20+ login failures from one IP in 5 minutes</description>
</rule>
<rule id="100512" level="12" frequency="5" timeframe="300">
<if_matched_sid>100510</if_matched_sid>
<same_field>sourceIp</same_field>
<different_field>actor</different_field>
<description>5+ distinct usernames from one IP in 5 minutes (credential stuffing)</description>
</rule> # KQL
ThomaSecurity_CL
| where eventType_s == "auth.login.failure"
| where TimeGenerated > ago(5m)
| summarize total_failures = count(), distinct_users = dcount(actor_s) by sourceIp_s
| where total_failures > 20 or distinct_users > 5 5. Account lockout (single-event signal)
What it means: Thoma's per-account lockout fired —
auth.account.locked is emitted exactly once per lockout event, after
MAX_FAILED_LOGINS consecutive failures. Per-event signal (no aggregation
needed). Useful as a complement to Rule 4: lockout means the in-product defense engaged;
investigate WHY the account hit the threshold.
# Splunk SPL
index=thoma sourcetype=thoma_security eventType=auth.account.locked
| stats count by orgId, actor, sourceIp, _time # Datadog
service:thoma @eventType:auth.account.locked @severity:warning # Wazuh
<rule id="100520" level="8">
<decoded_as>json</decoded_as>
<field name="eventType">auth.account.locked</field>
<description>Thoma account locked after MAX_FAILED_LOGINS consecutive failures</description>
</rule> # KQL
eventType: "auth.account.locked" and severity: "warning" 6. AD-driven user deactivation (offboarding signal)
What it means: Thoma's adDisabledUserSync scheduler ran and
auto-deactivated a Thoma user because their AD principal was disabled
(details.adStatus:'disabled') or absent (details.adStatus:'absent').
The SOC-visible mirror of your IT team's offboarding flow. Useful for cross-checking against
HR offboarding (every AD disable should produce a Thoma event within 24 hours) and detecting
orphan-AD-account scenarios.
# Splunk SPL
index=thoma sourcetype=thoma_security eventType=auth.user.deactivated_via_ad_sync
| stats count by orgId, entityId, details.adStatus, details.userEmail, _time # Datadog (orphan-account variant)
service:thoma @eventType:auth.user.deactivated_via_ad_sync @details.adStatus:absent # Wazuh โ base + orphan-elevated variant
<rule id="100530" level="6">
<decoded_as>json</decoded_as>
<field name="eventType">auth.user.deactivated_via_ad_sync</field>
<description>Thoma user auto-deactivated by AD-disabled-user sync</description>
</rule>
<rule id="100531" level="8">
<if_sid>100530</if_sid>
<field name="details.adStatus">absent</field>
<description>AD principal ABSENT (orphan or AD drift)</description>
</rule> # KQL
eventType: "auth.user.deactivated_via_ad_sync" and severity: "warning" OCSF v1.3.0 webhook output
Set SIEM_WEBHOOK_FORMAT=ocsf to flip webhook payloads to Open Cybersecurity
Schema Framework v1.3.0 — Splunk-OCSF, AWS Security Lake, Microsoft Sentinel (recent
versions), Datadog Cloud SIEM, and any tool that ingests v1.3.0 schema directly. The
original Thoma payload is preserved under unmapped.thoma_* and
raw_data for full-fidelity audit.
Class mapping: auth.login.success/failure → 3002 Authentication;
auth.account.locked → 3001 Account Change / 9 Lock;
auth.user.deactivated_via_ad_sync → 3001 Account Change / 5 Disable.
Severity maps Thoma 6-level scale to OCSF 0-6. Unknown event types fall back to
class_uid:0 so the SIEM still receives the event with the original payload.
Operator setup
Customer admin sets these env vars on the Thoma backend service to point at their SIEM:
# Webhook (Splunk HEC, Datadog, custom JSON receiver, etc.)
SIEM_WEBHOOK_URL=https://splunk.yourcompany.com:8088/services/collector/event
SIEM_WEBHOOK_AUTH_HEADER="Splunk <hec-token>"
SIEM_WEBHOOK_MIN_SEVERITY=info
SIEM_WEBHOOK_FORMAT=thoma # 'thoma' (default) | 'ocsf'
# Per-channel webhook overrides (route different events to different destinations)
AUDIT_SIEM_WEBHOOK_URL=https://...
ESIG_SIEM_WEBHOOK_URL=https://...
VAULT_SIEM_WEBHOOK_URL=https://...
AI_QUOTA_SIEM_WEBHOOK_URL=https://...
# Syslog UDP (Microsoft Sentinel, IBM QRadar, ArcSight, syslog-ng / rsyslog)
SIEM_SYSLOG_HOST=siem.yourcompany.com
SIEM_SYSLOG_PORT=514
SIEM_SYSLOG_FORMAT=cef # 'cef' | 'rfc5424'
SIEM_SYSLOG_MIN_SEVERITY=info
Restart the Thoma backend service after editing .env. Test by triggering a
deliberate failed login — the event should arrive in your SIEM within seconds. If
neither SIEM_WEBHOOK_URL nor SIEM_SYSLOG_HOST is set, the
forwarder is fully inert (zero overhead). Failures in either channel are logged at
console.warn level on the Thoma backend but never block authentication or any
other call site (fire-and-forget design).
Need more detail?
The full event JSON shapes (auth events + chain-break payloads), CEF + RFC 5424 syslog
format examples, OCSF mapping tables with severity reference, and detailed alert-state
auto-close guidance for *_restored events all live in the in-repo briefing
Briefings/Claude/siem-detection-rules.md — available to security teams
with source-code access.
Request source-code access to read the full briefing + run static-analysis tooling against the security-event surfaces.
Why Thoma ships these
Most PLM tools log to a database table and call it done. Thoma additionally fans security-relevant events out to your SIEM stack via webhook + syslog (and OCSF v1.3.0 when you want it), so your SOC sees offboarding, chain-integrity, and brute-force signals in the same incident pipeline as the rest of your stack — not in a separate admin panel nobody watches.