Mergen · Detection
Rule Reference
Mergen ships 54 built-in detection rules (40 PostgreSQL/system + 14 MongoDB) plus a declarative, admin-authored custom-rule engine. What each rule catches, its severity, and its license tier are below.
Critical · Warning · Info Tier: basic (always on) · advanced (advanced_analytics license)
How detection works
The agent captures the wire protocol (eBPF uprobes in-line, or AF_PACKET network capture / off-host collector), parses each statement into an AST (statement kind, tables read/written, columns, WHERE predicates, function calls), and evaluates every rule against that parsed stream — queries, bind parameters, result rows, and errors. For MongoDB, OP_MSG commands (compressed included) are parsed to verb + collection + filter shape.
Built-in rules
Injection
5 | sql_injection | crit | basic | Stacked queries (;-chained), UNION-based injection, and WHERE tautologies (1=1, 'a'='a'). |
| jsonb_filter_bypass | warn | advanced | JSONB @> containment against an empty container — an always-true filter bypass. |
| hibernate_native_query_marker | info | advanced | ORM native-query comment markers — inventories raw SQL escaping the ORM (injection surface). |
| mongo_where_injection Mongo | crit | basic | $where server-side JavaScript in filters (NoSQL injection). |
| mongo_js_execution Mongo | crit | basic | eval / mapReduce server-side JavaScript execution. |
Recon
4 | system_catalog_recon | crit | advanced | Reads of system catalogs; credential/role catalogs (pg_shadow, pg_authid) are Critical. |
| large_object_read | warn | basic | Direct pg_largeobject reads — bypasses lo_* permission checks. |
| mongo_unfiltered_read Mongo | warn | basic | find / count / distinct without a filter. |
| mongo_enumeration Mongo | info | basic | listDatabases / listCollections enumeration. |
Exfiltration
9 | excessive_data_export | warn | advanced | SELECT * with no effective row filter (CTE- and subquery-aware). |
| exfil_direct | warn | advanced | COPY TO, pg_read_file, pg_read_binary_file, pg_ls_dir, lo_export, lo_get, lo_put. |
| exfil_byte_volume | warn | advanced | Cumulative result-byte volume over a threshold in a window — slow-drain exfiltration. |
| exfil_time_pattern | warn | advanced | Regular-interval repeated querying (beaconing / scheduled scraping). |
| dblink_usage | warn | advanced | dblink() cross-database access — lateral movement and SSRF surface. |
| foreign_data_access | crit | advanced | FDW surface: CREATE SERVER / USER MAPPING, foreign tables, file_fdw file reads. |
| mongo_unfiltered_delete Mongo | crit | basic | delete with an empty filter — collection-wide destruction. |
| mongo_unfiltered_update Mongo | crit | basic | update with an empty filter — collection-wide modification. |
| mongo_excessive_data_export Mongo | warn | basic | Collection-wide read/export patterns. |
Compliance / PII
5 | pii_content | warn | basic | PII (SSN, card, e-mail…) in query literals, bind parameters, or result rows. |
| turkish_pii | crit | basic | KVKK Turkish PII: TCKN, IBAN, GSM, plate — with validity checks (TCKN checksum). |
| ozel_nitelikli_veri_access | crit | basic | KVKK special-category (health, religion, biometric…) data access. |
| sensitive_column_read | warn | basic | Reads touching operator-tagged sensitive columns (configurable list). |
| mongo_pii_in_response Mongo | warn | basic | PII patterns in response documents. |
Privilege
9 | role_privilege_escalation | crit | advanced | CREATE/ALTER ROLE SUPERUSER / CREATEROLE / BYPASSRLS (crit); REPLICATION (warn). |
| role_impersonation | warn | advanced | SET SESSION AUTHORIZATION / SET ROLE to a high-privilege role. |
| priv_esc_chain | crit | advanced | Multi-step privilege-escalation chains correlated within a session window. |
| dangerous_grant | crit | basic | GRANT of pg_execute_server_program / pg_read_all_data… (crit); GRANT TO PUBLIC (warn). |
| ddl_by_non_admin | crit | basic | DDL executed by a user outside the configured admin allowlist. |
| security_definer_function | warn | advanced | CREATE FUNCTION … SECURITY DEFINER — persistent privilege-escalation surface. |
| dangerous_extension | crit | advanced | CREATE EXTENSION untrusted-language (plpython3u, plperlu…). |
| mongo_auth_bypass Mongo | crit | basic | Comparison-operator injection on sensitive fields ({password:{$ne:""}}). |
| mongo_privileged_op Mongo | crit | basic | createUser / dropUser / role grants / createRole … |
DoS
6 | resource_abuse | warn | basic | Backend-stalling / row-byte-exploding primitives: pg_sleep, generate_series bombs. |
| cartesian_product | warn | basic | Unfiltered multi-table cartesian products. |
| unbounded_recursive_cte | warn | basic | WITH RECURSIVE without a top-level LIMIT. |
| redos | warn | basic | Catastrophic-backtracking regex operands (nested quantifiers). |
| lock_abuse | warn | basic | Explicit LOCK TABLE … IN ACCESS EXCLUSIVE MODE. |
| failed_login_burst | crit | basic | N auth failures within a sliding window (default 5 in 60s). |
DDL / Persistence
7 | dangerous_ddl | crit | basic | COPY FROM/TO PROGRAM (RCE), untrusted-language CREATE FUNCTION, risky DDL. |
| trigger_backdoor | warn | advanced | CREATE EVENT TRIGGER — DDL-fired persistence. |
| audit_tamper | crit | advanced | ALTER SYSTEM on logging/replication; archive_command / *_preload_libraries (crit). |
| mongo_destructive_command Mongo | crit | basic | drop, dropDatabase, dropIndexes, renameCollection, dropAll*. |
| mongo_admin_command Mongo | warn | basic | replSet* / shard / fsync / compact cluster administration. |
| mongo_profiling_change Mongo | warn | basic | Profile-level changes (audit-trail tampering). |
| mongo_session_kill Mongo | warn | basic | killSessions / killOp — session/operation termination. |
Anomaly
5 | off_hours_query | info | basic | Activity outside the configured business-hours window. |
| bulk_delete | crit | basic | DELETE without WHERE (full-table wipe); tables can be allowlisted. |
| large_result_set | info | basic | A single query returning an unusually large number of rows. |
| ueba_anomaly | crit | advanced | Per-entity behavioral baseline: EWMA + robust z-score over rate/tables/bytes/off-hours/write-ratio/new-table. |
| ueba_peer_anomaly | crit | advanced | Peer-group outlier vs application cohort (median + MAD robust statistics). |
Audit
4 | login_audit | info | basic | Per-login audit records: login_success (Info) / login_failed (Warning) with user/db/IP. |
| query_error | info | basic | Failed statements with SQLSTATE + message (permission-denied etc. are Warning). |
| clock_anchor_skew | warn | basic | Advisory when agent clock skew vs DB / console exceeds 5 minutes (audit-time integrity). |
| framer_desync_burst | warn | advanced | Bursts of wire-protocol desync — capture-integrity / evasion signal. |
Custom rules
Beyond the built-ins, admins author declarative custom rules in the console (Rules → Custom rules). They are pushed to agents over the heartbeat channel (version-gated, no restart) and evaluated on the same parsed streams as built-in rules. No code execution: matching is purely declarative, and regexes use a linear-time (RE2-class) engine — a custom rule cannot ReDoS the agent.
{
"id": "crown_jewel_read",
"severity": "critical", // info | warning | critical
"db_engine": "pg", // pg | mongo
"enabled": true,
"match": {
"statement_kinds": ["Select"],
"tables_regex": "^payroll_",
"columns_regex": "salary|iban",
"command_regex": "", // raw-SQL escape hatch
"user_in": [],
"user_not_in": ["app_readonly"],
"require_no_where": false
},
"message_template": "payroll read: {kind} on {tables} by {user}",
"rate": { "count": 10, "window_secs": 60, "group_by": "user" }
}
Fields
| id | Stable rule identifier; appears as rule_id on detections; disable per host/group/global like any built-in. |
| severity | info | warning | critical (aliases warn / crit; anything else falls back to info). |
| db_engine | pg (parsed SQL stream) or mongo (parsed MongoDB command stream). |
| enabled | A false definition is never compiled. |
| match | The match conditions (AND semantics). An empty object matches everything. |
| message_template | Detection message; placeholders below. |
| rate | If present: a count-in-window threshold. |
Match conditions (AND — every present condition must hold)
An empty match matches every statement — always constrain at least one axis.
| statement_kinds | Parsed statement kind (Select, Insert, Update, Delete, Copy, CreateRole, AlterRole, Grant). Empty = any. |
| tables_regex | Any table the statement reads or writes. |
| columns_regex | Any column the statement reads. |
| command_regex | The raw SQL text — the escape hatch when AST fields are not enough. |
| user_in / user_not_in | Session user allow / deny (exact match). |
| require_no_where | Fires only when the statement has NO WHERE predicates (full-table shape). |
| time_window | Scopes the rule to a UTC time window: {start, end (HH:MM), negate, days}. start>end wraps overnight; negate matches OUTSIDE the window; days Mon=0..Sun=6. Absent = any time. |
MongoDB (db_engine: "mongo")
command_regex → command verb, tables_regex → collection, require_no_where → no filter document. Other match fields do not apply.
Message template placeholders
{kind} · {tables} · {columns} · {user}
Rate
With rate, the rule fires only when it matches ≥ count times within window_secs (sliding window). group_by:"user" keeps a per-user window; otherwise it is global.
Safety
An invalid regex skips that whole rule (logged) — never a crash or half-match. Severity aliases warn/crit; unknown → info. Scope (enable/disable per host/group/global) and alerting work exactly like built-in rules.