Custom PII filters

The built-in detectors cover four shapes: email, card-like number, US SSN, and phone. Everything specific to your organisation — employee IDs, customer account numbers, internal ticket references, case numbers — needs a pattern you write.

A pii_redact rule with a --pattern uses your regex instead of the built-ins.

vulnetix ai-firewall policy guardrail redact-employee-ids \
  --rule-type pii_redact \
  --action redact \
  --pattern '\bEMP-[0-9]{6}\b' \
  --priority 25 \
  --enable
Warning A custom pattern replaces the built-ins for that rule — it does not add to them. If you want both your employee IDs and the built-in detectors, you need two rules: one with a pattern, one without.
# Rule 1 — the built-ins (no --pattern)
vulnetix ai-firewall policy guardrail redact-pii \
  --rule-type pii_redact --action redact --priority 20 --enable

# Rule 2 — your own identifiers
vulnetix ai-firewall policy guardrail redact-employee-ids \
  --rule-type pii_redact --action redact \
  --pattern '\bEMP-[0-9]{6}\b' --priority 25 --enable

Both run. Both redact to [REDACTED].

Patterns worth writing

Adapt the shapes; the point is the anchoring.

Internal identifiers

\bEMP-[0-9]{6}\b

An employee ID. Note the \b word boundaries — without them this matches inside longer strings and will fire on things you did not intend.

\bACCT-[0-9]{4}-[0-9]{4}-[0-9]{4}\b

A customer account number.

\b(INC|CHG|PRB)[0-9]{7}\b

ServiceNow-style ticket references. Whether this is sensitive depends on your organisation — often it is a flag, not a redact, because leaking the existence of an incident number is different from leaking its contents.

National identifiers other than US SSN

The built-in SSN detector is US-only. If you operate elsewhere you need your own:

\b[0-9]{3}-[0-9]{3}-[0-9]{3}\b

Canadian SIN, hyphenated form.

\b[A-CEGHJ-PR-TW-Z]{2}[0-9]{6}[A-D]\b

UK National Insurance number.

Note Be careful with short numeric identifiers. A pattern like \b[0-9]{9}\b will match an enormous amount of ordinary text — port numbers, timestamps, row counts — and your developers will notice. Anchor on a prefix, a separator, or a checksum-bearing shape wherever you can.

Internal URLs and hostnames

https?://[\w.\-]+\.(internal|corp)(/[^\s]*)?

Usually a flag — you want to know it is happening — or a redact if the URL itself reveals customer identifiers in its path.

Match what you mean

Two habits that prevent most false positives:

Anchor on structure, not on length. \bEMP-[0-9]{6}\b is safe. [0-9]{6} is not — it matches inside a phone number, a timestamp, and a git SHA that happens to be numeric.

Use word boundaries. \b on both ends of an identifier stops it matching inside a longer token. Without it, ACCT-1234 matches inside MY-ACCT-12345678, and the redaction cuts the string in half.

Regex constraints

Patterns are RE2:

  • No lookahead or lookbehind. You cannot write (?<!prefix).
  • No backreferences. You cannot write (\w+)-\1.
  • Everything else you are used to — character classes, quantifiers, alternation, anchors, named groups, (?i) — works.

This is not an oversight. RE2 guarantees linear-time matching, which means no pattern you write, and no input an attacker sends, can make the firewall hang. A regex engine that can be made to hang on a crafted input is a denial-of-service vector sitting in the request path of every LLM call your organisation makes.

An uncompilable pattern is silently skipped — the rule looks enabled and enforces nothing. Always run:

vulnetix ai-firewall status

after a rule change. It compiles every pattern locally and reports guardrail_pattern_invalid with a hint. See the regex reference.

Testing a pattern

The engine uses Go’s RE2. The most reliable way to test a pattern before you deploy it is a Go regex playground, or:

# Adopt it at flag first, then look at what it actually caught
vulnetix ai-firewall policy guardrail redact-employee-ids \
  --rule-type pii_redact --action flag \
  --pattern '\bEMP-[0-9]{6}\b' --priority 25 --enable

flag records the match and forwards the request untouched, so you get the signal with no risk of rewriting traffic you did not mean to. See rollout.