Mask PII in action input
PiiInputValidator and PiiInputSanitizer plug into @InputGuardrail to reject or mask personal data before an action sees it. Both are in tnsai-core.
This page covers action input. To check traced text, see PiiGuardrail in Guardrails. To mask data on its way to logs, events and memory, see Redaction.
Mask PII before the action runs
public static class ContactActions {
@ActionSpec(type = ActionType.LOCAL, description = "Forward a contact message")
@InputGuardrail(
validators = PiiInputValidator.class,
sanitizers = PiiInputSanitizer.class,
onFailure = InputGuardrail.FailureAction.SANITIZE)
public String handleContact(String message) {
return message;
}
}A call with "Mail me at jane@example.com" reaches the action as "Mail me at [REDACTED_EMAIL]".
Validators and sanitizers need a public no-argument constructor. To add your own, implement InputValidator (isValid) or InputSanitizer (sanitize).
What counts as PII
Detection is a high-precision heuristic, not a legal or jurisdiction-complete classifier.
| Detects | Replaced with |
|---|---|
| Email addresses | [REDACTED_EMAIL] |
| Phone numbers with an international prefix, or 10-digit local numbers | [REDACTED_PHONE] |
| IBANs (country code and check digits) | [REDACTED_IBAN] |
| Payment card numbers of 13 to 19 digits that pass the Luhn check | [REDACTED_PAYMENT_CARD] |
US social security numbers (123-45-6789) | [REDACTED_NATIONAL_ID] |
| Turkish identity numbers with a valid checksum | [REDACTED_NATIONAL_ID] |
PiiInputValidator rejects a value as soon as one class matches. It also checks parameter names, so a name that contains PII is rejected too.
When a value is rejected
onFailure decides what happens. The default is REJECT.
onFailure | Result |
|---|---|
REJECT | The call fails |
SANITIZE | The sanitizers run in order, then the validators check the result again |
REVIEW | The call fails and is marked as needing review |
WARN | A warning is logged and the original value passes through |
A failed call surfaces as an ActionExecutionException in the VALIDATION category. Its cause is a GuardrailViolationException whose guardrailCause() is INPUT_VALIDATOR_REJECTED, or NEEDS_REVIEW for REVIEW. Diagnostics never include the matched text.
Strings are checked directly. Booleans, enums that render as their name, and standard JDK number types are checked in their text form. Lists, maps, arrays, other objects and enums with a custom toString cannot be rewritten safely, so they are rejected under every policy except WARN. Flatten such payloads into string parameters first.
Where the check runs
The executor runs the guardrail after @BeforeAction and before any parameter encryption from @Security, so validators always see plain text. The same check applies to role actions, @Tool methods and MCP tools.
Pattern limits
blockPatterns and allowPatterns on the same annotation use a restricted regex subset, to avoid slow matching:
- each pattern and each inspected value is limited to 1,024 characters;
- backreferences, nested or repeated quantifiers, comments mode and more than one alternation group are not allowed.
The built-in PII patterns are not subject to these limits.