Skip to main content

Matchers

As you have seen in PII Redaction, matchers tell the transform engine what to act on. To allow for more precise configuration, matchers are split into two types: common fields and matching fields. There can only be one matching field, but there can be many common fields.

Common fields

Common fields are fields that you don’t redact, and are only used for the purpose of finding matching spans. For common fields, you can supply zero or more of the following:
  • direction: either inbound or outbound
  • method: a list of HTTP methods like GET
  • pathPattern: a regexp to match on the URL path
  • host: a regexp to match on the hostname

Matching fields

Matching fields are fields that you intend to redact. Only one can be present so that what will be modified is clear. For matching fields, you can only supply one of the following:
  • jsonPath: a JSONPath expression to match on JSON formatted bodies (e.g. $.user.password)
  • queryParam: the name of a query parameter
  • headerName: the name of a header
  • urlPath: if set to true, matches on the entire url path
  • fullBody: if set to true, matches on the entire body
The presence of urlPath may seem strange given that we have pathPattern but you can use this to redact all paths without having to provide a pathPattern.

Actions

Actions specify how to mutate the matching span. Tusk currently supports these actions:
  1. redact. This replaces the value with a hash. Accepts a hashPrefix field if you need some form of identifier.
  2. mask. This replaces the value with a repeated character mashChar (defaults to ’*’). Use this for fixed length strings where redact won’t work.
  3. replace. Replaces the string with replaceWith. This can be used for things like testing tokens, etc.
  4. drop. Deletes all useful data (body, header, etc.) from the span and mark it as dropped. The span isn’t deleted entirely so that tests depending on it can still get mocked.

Examples

1. Inbound Requests

Example: Redact

{
  "matcher": {
    "direction": "inbound",
    "method": "POST",
    "pathPattern": "/api/auth/login",
    "jsonPath": "$.password"
  },
  "action": {
    "type": "redact"
  }
}
Before: {"username": "john@example.com", "password": "secretPassword123"} After: {"username": "john@example.com", "password": "REDACTED_a7b9c1d2e3f4..."}

Example: Mask

{
  "matcher": {
    "direction": "inbound",
    "pathPattern": "/api/user/lookup",
    "queryParam": "ssn"
  },
  "action": {
    "type": "mask",
    "maskChar": "X",
  }
}
Before: /api/user/lookup?ssn=123-45-6789&name=john After: /api/user/lookup?ssn=XXXXXXXXXXX&name=john

Example: Replace

{
  "matcher": {
    "direction": "inbound",
    "headerName": "Authorization"
  },
  "action": {
    "type": "replace",
    "replaceWith": "Bearer test-token-12345"
  }
}
Before: Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9... After: Authorization: Bearer test-token-12345

Example: Drop

{
  "matcher": {
    "direction": "inbound",
    "pathPattern": "/admin/internal/*"
  },
  "action": {
    "type": "drop"
  }
}
Before: Span recorded for /admin/internal/user-data After: Empty span recorded for /admin/internal/user-data

Example: Redact

{
  "matcher": {
    "direction": "inbound",
    "pathPattern": "/api/user/profile",
    "jsonPath": "$.data.creditCard"
  },
  "action": {
    "type": "redact"
  }
}
Before: {"data": {"name": "John", "creditCard": "4111-1111-1111-1111"}} After: {"data": {"name": "John", "creditCard": "REDACTED_b8c9d1e2f3g4..."}}

Example: Mask

{
  "matcher": {
    "direction": "inbound",
    "pathPattern": "/api/users",
    "jsonPath": "$.users[*].phone"
  },
  "action": {
    "type": "mask",
    "maskChar": "*",
  }
}
Before: {"users": [{"name": "John", "phone": "+1-555-123-4567"}]} After: {"users": [{"name": "John", "phone": "***-***-****"}]}

2. Outbound Request

Example: Redact

{
  "matcher": {
    "direction": "outbound",
    "host": "api.stripe.com",
    "headerName": "Authorization"
  },
  "action": {
    "type": "redact"
  },
  "description": "Redact Stripe API keys"
}
Before: Authorization: Bearer sk_live_51234567890abcdef... After: Authorization: REDACTED_c1d2e3f4a5b6...

Example: Mask

{
  "matcher": {
    "direction": "outbound",
    "host": "payments.example.com",
    "method": "POST",
    "jsonPath": "$.customer.creditCard.number"
  },
  "action": {
    "type": "mask",
    "maskChar": "*",
  }
}
Before: {"customer": {"creditCard": {"number": "4111111111111111"}}} After: {"customer": {"creditCard": {"number": "****************"}}}

Example: Replace

{
  "matcher": {
    "direction": "outbound",
    "host": "database.internal.com",
    "jsonPath": "$.auth.password"
  },
  "action": {
    "type": "replace",
    "replaceWith": "test-db-password"
  }
}
  • Before: {"auth": {"username": "dbuser", "password": "prod-secret-123"}}
  • After: {"auth": {"username": "dbuser", "password": "test-db-password"}}

Example: Redact

{
  "matcher": {
    "direction": "outbound",
    "host": "api.external-service.com",
    "jsonPath": "$.users[*].email"
  },
  "action": {
    "type": "redact"
  }
}
Before: {"users": [{"id": 123, "email": "user@example.com"}]} After: {"users": [{"id": 123, "email": "REDACTED_d1e2f3g4h5i6..."}]}

Example: Mask

{
  "matcher": {
    "direction": "outbound",
    "host": "api.bank.com",
    "jsonPath": "$.accounts[*].accountNumber"
  },
  "action": {
    "type": "mask",
    "maskChar": "X",
  }
}
Before: {"accounts": [{"type": "checking", "accountNumber": "1234567890123456"}]} After: {"accounts": [{"type": "checking", "accountNumber": "XXXXXXXXXXXXXXXX"}]}
I