DLP Pattern Authoring
DLP pattern authoring lets you decide what data may flow through your agents. It is the content layer of MITRITY DLP (data loss prevention): it governs what data is allowed to flow. Its companion is destination allowlists, the destination layer of DLP, which governs where data can flow.
The Two-Layer Model
A DLP pattern is a reusable definition: a regex, a list of literal strings or a built-in classifier. A pattern doesn't do anything on its own. To enforce it, you attach the pattern to a policy with a binding. The binding carries the per-policy settings: direction, action, priority and an enabled flag. The same pattern can be bound to many policies, each with different settings.
dlp_patterns (definitions)
│ M:N via bindings
▼
policy_dlp_bindings (per-policy: direction + action + priority + enabled)
│
▼
agents.policy_id → policies → bindings → patterns
This separation matters in practice. The security team curates a tenant-wide catalog of patterns once. Individual policy owners then decide which patterns apply to their agents, and how strict the enforcement should be.
Where to Find It
- Go to /app/policies.
- Expand a policy row.
- Open the DLP sub-tab.
The tab has two sections:
- Built-in patterns: the MITRITY-maintained catalog covering common PII and credential formats.
- Custom patterns: patterns your tenant has authored. Members and above can create, edit and delete entries here.
Each section lists the bindings currently attached to this policy. To add a binding, select a pattern from the pattern picker. The binding inherits sensible defaults, which you can tune inline: direction both, action flag, priority 50.
Built-in Pattern Catalog
MITRITY ships a curated set of detectors that cover the most common PII and credential formats. MITRITY maintains them, and customers cannot edit the underlying detection logic. You bind them, and you configure direction, action and priority on the binding.
| Pattern | Type | Category | Detects |
|---|---|---|---|
ssn_us | classifier | pii | US SSNs with structural validation (area not 000/666/9XX; group not 00; serial not 0000) |
personnummer_se | classifier | pii | Swedish personal numbers (YYMMDD + 4 digits, real date, Luhn-style checksum) |
credit_card | classifier | pii | Credit cards 13–19 digits, Luhn-validated |
iban | classifier | pii | IBAN with mod-97 checksum |
email | regex | pii | RFC 5322 simplified |
phone | classifier | pii | E.164 + common national formats |
ipv4 | regex | pii | Dotted-quad IPv4 addresses |
aws_access_key | regex | credentials | AKIA[0-9A-Z]{16} |
github_token | regex | credentials | ghp_ / gho_ / ghu_ / ghs_ / ghr_ prefixes |
generic_api_key | classifier | credentials | Entropy + prefix heuristic. Low-confidence: recommended flag action only |
The classifier-backed built-ins do more than a regex match. They apply structural validation (Luhn, mod-97, real-date checks) to keep false positives manageable. The generic_api_key classifier is intentionally fuzzy, so bind it with action=flag: you keep visibility without blocking legitimate traffic.
Custom Patterns
There are two types of customer-authored pattern.
regex
A single regular expression with an optional flags field. Only the i flag (case-insensitive) is accepted. Every regex is:
- Compile-tested at save time. Invalid regexes are rejected with the underlying parse error.
- ReDoS-fuzzed with a 10 KB pathological input, under a 100 ms budget. This catches regular expression denial of service (ReDoS): a regex that takes extremely long on some inputs. Patterns that time out are rejected before they can ever run in production.
{
"name": "internal_project_id",
"pattern_type": "regex",
"pattern_data": { "pattern": "PRJ-[A-Z0-9]{8}", "flags": "" },
"category": "internal"
}
string_list
A literal list of strings, with an optional case_insensitive flag. There are no regex semantics: characters like ., * and ? are matched literally. This type is best for competitor names, internal product codenames, legal-hold phrases and project keywords.
{
"name": "marketing_competitors",
"pattern_type": "string_list",
"pattern_data": { "strings": ["CompetitorA", "CompetitorB"], "case_insensitive": true },
"category": "competitor"
}
classifier
Reserved for MITRITY-maintained built-ins. A POST or PUT with pattern_type=classifier returns 400 Bad Request.
Bindings — Direction, Action, Priority
A binding is the configurable attachment between a policy and a pattern. Four fields control its behavior:
- direction:
inbound(data flowing into the agent, e.g. tool responses),outbound(data leaving the agent, e.g. tool arguments) orboth. Defaults toboth. - action:
block(deny the call),redact(replace each match with[REDACTED-<pattern_name>]before the payload is forwarded) orflag(audit-only, no payload modification). Defaults toflag. - priority: an integer. Higher evaluates first, and the first match wins. Defaults to 50. Use it to make high-confidence patterns shadow lower-confidence ones on the same payload.
- enabled: a boolean. Disable a binding temporarily without deleting it.
Worked Example
Here is the same marketing_competitors pattern in two different policies, with two different behaviors:
- Marketing policy: bind with
direction=outbound, action=block. Agents can research competitors, because tool responses come in freely, but they cannot publish anything that contains competitor names. - Support policy: bind with
direction=both, action=redact. Support replies that contain competitor names are redacted automatically before they reach the customer. The same applies to incoming customer messages that mention competitors, in case the agent quotes them back.
That reuse, one pattern definition with several policy bindings for different intents, is the value of the two-layer model.
Direction Inference
The Edge Node has to decide which side of an MCP exchange to inspect. It infers inbound or outbound from the action type, with a heuristic: action types whose name contains any of send, post, put, write, upload, push, publish or forward classify as outbound. Everything else classifies as inbound.
This is edge behavior, not part of the API contract. The heuristic may evolve as we observe more tool categories. If you're not sure how a particular tool action classifies, bind with direction=both, and the binding fires on either side of the exchange.
GDPR-Safe Audit Contract
This rule has no exceptions: DLP audit events never store the raw matched content. The matched substring is the most sensitive part of the request. It is the very thing the policy is there to protect, so it must not be persisted in the audit store.
Every DLP audit event carries enough structural information to investigate a match, without persisting the matched substring itself:
{
"id": "evt_...",
"tenant_id": "...",
"agent_id": "...",
"policy_id": "...",
"pattern_id": "ssn_us",
"pattern_name": "SSN (US)",
"direction": "outbound",
"action_taken": "blocked",
"detection_reason": "pattern_match",
"position": 247,
"length": 11,
"tool_pattern_matched": "drive:create",
"occurred_at": "2026-05-26T10:14:00Z"
}
There is no value, no matched_text and no other field that carries raw content. The boundary between the detection engine and the audit reporter strips any such field defensively. Even if a future engine change accidentally tried to attach raw content, the reporter would drop it before persistence.
Mapping Binding Action to Edge Behavior and Audit Outcome
| Binding action | Edge behavior | action_taken value |
|---|---|---|
block | Tool call denied with JSON-RPC error returned to the caller | blocked |
redact | Match replaced with [REDACTED-<pattern_name>] before payload forwarded downstream | redacted |
flag | Match passes through unchanged; event recorded for review | flagged |
A binding match is recorded with detection_reason set to pattern_match, and with action_taken set as in the table above.
A block binding that the edge cannot evaluate, such as one with a malformed pattern or an unknown classifier, fails closed: the action is blocked, and the event is recorded as pattern_match with action_taken: blocked. Its audit event keeps the edge's own reason, pattern_eval_failed, in the dlp_detection_reason_raw metadata.
The edge's built-in classification records the other reasons: unauthorized_destination, sensitive_exfil and accumulation, with action_taken set to blocked, alerted or logged (see DLP Event Types).
DLP events are not forwarded to SIEM destinations. A SIEM destination receives the agent's action and its decision, for example blocked, not the DLP event.
Coexistence with Destination Allowlists
DLP has two layers that work together:
- Pattern authoring (this page) governs what the agent sends, with content-level inspection of the payload.
- Destination allowlists govern where the agent sends, with endpoint-level enforcement of the destination URL.
A request that carries a credit-card number to an unapproved endpoint trips both layers. Either layer alone is meaningful enforcement, and together they form defence in depth: a misconfigured allowlist won't leak content patterns, and a missing content pattern won't reach an unapproved destination.
API Reference
All DLP pattern and binding endpoints are mounted under /api/v1. Bearer-token auth is required throughout; see API Overview for the auth scheme.
Patterns
| Method & Path | Purpose |
|---|---|
GET /api/v1/dlp/patterns | List patterns visible to the tenant (filters: pattern_type, category, is_builtin, search). |
POST /api/v1/dlp/patterns | Create a custom pattern. pattern_type must be regex or string_list. |
PUT /api/v1/dlp/patterns/{id} | Update a custom pattern. Built-ins return 403 Forbidden. |
DELETE /api/v1/dlp/patterns/{id} | Delete a custom pattern. Built-ins return 403 Forbidden. Existing bindings to the pattern are removed as well. |
Bindings
| Method & Path | Purpose |
|---|---|
GET /api/v1/policies/{policy_id}/dlp-bindings | List bindings for a policy. |
POST /api/v1/policies/{policy_id}/dlp-bindings | Create a binding. Body carries pattern_id, direction, action, priority, enabled. |
PUT /api/v1/policies/{policy_id}/dlp-bindings/{binding_id} | Update binding fields. |
DELETE /api/v1/policies/{policy_id}/dlp-bindings/{binding_id} | Remove the binding. The underlying pattern is untouched. |
The full request and response schemas are in the API reference.
Best Practices
Start with the Built-in Catalog
The built-in patterns cover ~80% of real-world PII and credential exfiltration. Bind them to your most permissive policies first, with action=flag. Watch a week of audit events, then promote the high-signal ones to redact or block.
Author Custom Patterns at the Tenant Level, Bind at the Policy Level
A pattern like internal_project_id is a tenant-wide fact. Author it once, then let individual policy owners decide whether it applies to their agents. Don't fork the pattern per policy: use the binding settings to vary the behavior.
Prefer string_list Over regex When You Can
string_list patterns can't be mis-authored into a ReDoS hazard. They are also easier for non-engineers to maintain, and faster to evaluate at the edge. Use regex only when you need structural matching.
Use Priority to Resolve Overlap
When two patterns can match the same content (e.g. a custom regex and a built-in classifier), the higher-priority binding wins. Give high-confidence patterns a higher priority so that they shadow fuzzy ones. The pattern_id recorded on the audit event is then the one that best describes the match.
Pair with Destination Allowlists
A pattern binding without a destination allowlist catches content but not destinations. An allowlist without pattern bindings catches destinations but not content. The steady state is both layers on every policy.
Related Documentation
- Destination Allowlists: the DLP destination layer
- Threat Intelligence: cross-tenant threat indicators
- Injection Detection: prompt injection detection
- API Overview: Bearer-token auth and conventions