Skip to main content
Sign in →

Custom DLP Patterns

Define organisation-specific detection rules that run on top of ShieldAgent's built-in PII, credential, and financial data coverage.

What Custom Patterns Are

Custom DLP patterns let your team define additional sensitive-data detection rules specific to your organisation — for example, internal project codes, proprietary identifiers, or domain-specific data formats. Each pattern is a RE2-compatible regular expression paired with an action (block, redact, alert, or shadow). Custom patterns are evaluated in sequence after built-in rules, so they extend rather than replace the standard coverage.

Plan Comparison

Custom pattern limits and workflow features depend on your ShieldAgent plan.

FeatureProBusiness
Maximum custom patterns2020
Available actionsblock, redact, alertblock, redact, alert
Activation approvalSingle approver4-eye (two approvers required)
Webhook delivery on matchYes, HMAC-signed

RE2 Syntax Limits

Patterns must be valid RE2 expressions. The following are not supported:

  • Back-references (\1, \2, …)
  • Lookahead and lookbehind assertions
  • Backreference-based recursion
  • PCRE possessive quantifiers

Maximum compiled pattern size is 256 KB. Overly broad patterns (e.g. .* with no anchors) are rejected at save time.

Full RE2 syntax reference

Test Before You Deploy

Use the dry-run endpoint to validate a pattern against sample traffic before activating it. Dry runs evaluate the pattern on up to 50 recent tool-call payloads captured in shadow mode. No action is taken — results are returned immediately.

  1. Open the dashboard, go to Security → DLP → Custom Patterns, then select New Pattern.
  2. Enter your RE2 expression and choose an action.
  3. Click Test Pattern to run a dry run against recent shadow-mode traffic.
  4. Review matched excerpts (values are partially masked) and adjust the pattern if needed.
  5. Click Save to submit the pattern for activation (subject to approval on Business plans).

Dry-run via SDK

You can also drive dry runs programmatically:

typescript
const result = await client.dlp.patterns.dryRun({
  expression: '\\bPROJ-[0-9]{4,6}\\b',
  action: 'alert',
  sampleSize: 50,
});

// result.matches — array of masked excerpts
// result.matchCount — total hits across sampled payloads
console.log(`Matched ${result.matchCount} times across ${result.sampleSize} payloads`);

Action Options

Each custom pattern is paired with one of five actions:

ActionWhen to use
blockReject the tool call immediately. Use for patterns that must never leave your perimeter.
redactReplace the matched value with a redaction token and forward the sanitised request. Use when the downstream tool still needs to receive a request but must not see the sensitive value.
alertAllow the request and generate an audit event. Use for monitoring when blocking would break a workflow you are still evaluating.
notifyAllow the request and deliver a signed webhook or Slack event to your configured endpoint (Business). On Pro, writes an in-app notification instead. No raw match content is transmitted — only a SHA-256 hash.
shadowRecord the match silently with no modification or alert surfaced to the agent. Business plan only. Use during rollout to validate pattern accuracy before switching to a more aggressive action.

4-Eye Approval (Business)

On Business plans, any new or modified custom pattern enters a pending state before it goes live. A second named approver — different from the pattern author — must review and approve it. This prevents a single administrator from activating an overly broad or incorrect pattern unilaterally.

What triggers itCreating a new custom pattern, editing an existing pattern (expression, action, or description), or re-activating a disabled pattern.
Who can approveAny user with the Security Administrator role, except the author of the pending change.
Audit trailThe pattern version, author, timestamp, approver, and approval timestamp are all recorded. The full approval chain is visible in Dashboard → Security → DLP → Custom Patterns → [pattern] → History.

Webhook Delivery

When a custom pattern with the notify action matches on a Business plan, ShieldAgent delivers a signed event to your configured endpoint. Each delivery carries an HMAC signature using your webhook secret. No raw match content is transmitted — only a SHA-256 hash of the matched value.

Webhook payload schema

json
{
  "event": "dlp.notify",
  "version": 1,
  "patternId": "550e8400-e29b-41d4-a716-446655440000",
  "patternName": "internal-project-codes",
  "action": "notify",
  "actionTaken": "notify",
  "tenantId": "550e8400-e29b-41d4-a716-446655440001",
  "agentId": "550e8400-e29b-41d4-a716-446655440002",
  "mcpServerId": "550e8400-e29b-41d4-a716-446655440003",
  "interactionId": "550e8400-e29b-41d4-a716-446655440004",
  "contentHashSha256": "a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3",
  "contentHash": "a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3",
  "severity": "high",
  "gdprClassification": "credential",
  "timestamp": "2026-05-30T14:22:00.000Z"
}

Field reference

FieldTypeNullableExampleDescription
eventstringno"dlp.notify"Always "dlp.notify". Use this to route the event in your handler.
versionnumberno1Schema version. Increments on breaking changes. Current version: 1.
patternIdstring (UUID)no"550e8400-…"ID of the custom pattern that triggered this event.
patternNamestringno"internal-project-codes"Human-readable name of the pattern, as configured in your dashboard.
actionstringno"notify"Always "notify" in this payload.
actionTakenstringno"notify"Alias for action. Included for schema symmetry with other event types.
tenantIdstring (UUID)no"550e8400-…"Your ShieldAgent workspace identifier.
agentIdstring (UUID)no"550e8400-…"Agent session that made the tool call triggering this match.
mcpServerIdstring (UUID)no"550e8400-…"The MCP server the call was routed to.
interactionIdstring (UUID)no"550e8400-…"Unique per tool-call interaction. Use as your idempotency key — retries carry the same ID.
contentHashSha256string (hex)no"a665a459…"SHA-256 hash of the matched text. The raw value is never transmitted.
contentHashstring (hex)no"a665a459…"Alias for contentHashSha256.
severity"critical" | "high" | "medium" | "low"no"high"Severity as classified by the matching pattern.
gdprClassification"pii" | "credential" | "financial" | "health" | "none"no"credential"GDPR data category of the matched content.
timestampstring (ISO 8601)no"2026-05-30T14:22:00.000Z"UTC timestamp when the event was generated.

HMAC verification

The signature is sent in the X-ShieldAgent-Signature header as sha256=<hex>. Always verify it before processing the event. Use a constant-time comparison to prevent timing attacks:

Node.js

typescript
import crypto from 'node:crypto';

export function verifySignature(rawBody: string, secret: string, header: string): boolean {
  if (!header.startsWith('sha256=')) return false;
  const expected =
    'sha256=' +
    crypto.createHmac('sha256', secret).update(rawBody, 'utf8').digest('hex');
  const a = Buffer.from(expected, 'utf8');
  const b = Buffer.from(header, 'utf8');
  if (a.length !== b.length) return false;
  return crypto.timingSafeEqual(a, b);
}

// Express: capture the raw body before JSON parsing
// app.use('/shieldagent-notify', express.raw({ type: 'application/json' }));
// app.post('/shieldagent-notify', (req, res) => {
//   if (!verifySignature(req.body.toString('utf8'), process.env.SHIELDAGENT_WEBHOOK_SECRET!, req.header('x-shieldagent-signature')!)) {
//     return res.status(401).end();
//   }
//   const payload = JSON.parse(req.body.toString('utf8'));
//   res.status(200).json({ ok: true });
// });

Python

python
import hmac, hashlib

def verify_signature(raw_body: bytes, secret: str, header: str | None) -> bool:
    if not header or not header.startswith('sha256='):
        return False
    expected = 'sha256=' + hmac.new(
        secret.encode('utf-8'), raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, header)

# FastAPI example:
# @app.post('/shieldagent-notify')
# async def handle(request: Request):
#     raw = await request.body()
#     if not verify_signature(raw, os.environ['SHIELDAGENT_WEBHOOK_SECRET'],
#                             request.headers.get('x-shieldagent-signature')):
#         raise HTTPException(401)
#     payload = json.loads(raw)

Test vector

Run your verifier against these values before going to production. Any difference means your implementation is incompatible with the production signing path:

text
secret = "shieldagent-test-secret-32ch-pad"
body   = '{"event":"dlp.notify","version":1,"hash":"deadbeef"}'
header = "sha256=8834a0520425c5b374ab3d444d3c0368cb6eb1af1a1678a2ed5e8937e8bcb06f"

Manage and rotate your webhook secret in Dashboard → Security → DLP → Notification Targets. Old signatures are invalidated immediately on rotation.

Slack Payload Schema

When your notification target is a Slack incoming webhook, ShieldAgent sends a Block Kit message instead of the canonical JSON payload. The X-ShieldAgent-Signature header is still present and is computed over the Block Kit body bytes — not the canonical payload. If your Slack receiver also verifies the signature, hash the raw request body exactly as received.

Block Kit example

json
{
  "text": ":warning: ShieldAgent DLP notify: `internal-project-codes` (high)",
  "blocks": [
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": ":warning: ShieldAgent DLP notify: `internal-project-codes` (high)"
      }
    },
    {
      "type": "section",
      "fields": [
        { "type": "mrkdwn", "text": "*Pattern*\ninternal-project-codes" },
        { "type": "mrkdwn", "text": "*GDPR class*\ncredential" },
        { "type": "mrkdwn", "text": "*Agent*\n`550e8400-e29b-41d4-a716-446655440002`" },
        { "type": "mrkdwn", "text": "*MCP server*\n`550e8400-e29b-41d4-a716-446655440003`" },
        { "type": "mrkdwn", "text": "*Interaction*\n`550e8400-e29b-41d4-a716-446655440004`" },
        { "type": "mrkdwn", "text": "*Content SHA-256*\n`a665a45920422f9d…`" }
      ]
    },
    {
      "type": "context",
      "elements": [
        { "type": "mrkdwn", "text": "event=dlp.notify version=1 ts=2026-05-30T14:22:00.000Z" }
      ]
    }
  ],
  "metadata": {
    "event_type": "dlp.notify",
    "event_payload": { "...": "full canonical payload — same fields as webhook schema above" }
  }
}

Top-level fields

FieldTypeDescription
textstringPlain-text summary shown in Slack notifications and link unfurls.
blocksarrayBlock Kit sections: a summary header, a detail fields section with pattern name, GDPR class, agent, MCP server, interaction ID, and content hash prefix, plus a context footer with event metadata.
metadata.event_type"dlp.notify"Structured event type for SIEM ingestion.
metadata.event_payloadobjectFull canonical DLP notify payload (same fields as the webhook schema above). Lets SIEM tooling index structured fields from a Slack-delivered event.
The X-ShieldAgent-Signature is computed over the Block Kit JSON as serialised on the wire — not the nested metadata.event_payload object. Always hash the raw request body.

What if I paste a Slack URL into a generic webhook target?

The API recognises Slack incoming-webhook URLs and automatically corrects the target kind from webhook to slack. The create or update call succeeds, and the response includes two extra fields:

Response fieldTypeDescription
autoCorrectedToSlackbooleantrue when the kind was automatically changed from webhook to slack.
warningsstring[]Contains "url_resembles_chat_platform_consider_dedicated_kind" when the URL was auto-corrected.

If you configure kind: webhook with a URL that resembles Discord or Microsoft Teams, the API returns the same warning without auto-correcting — the target is saved as webhook and delivery may fail. Set the correct kind explicitly at create time.

Shared Responsibility

ShieldAgent provides the infrastructure to create and enforce custom DLP patterns. You are responsible for ensuring that patterns are accurate, do not match unintended data, and comply with applicable laws and regulations in your jurisdiction. For the full customer configuration responsibilities, see the Terms of Service — Customer Configuration.
Custom DLP Patterns