JSON Path
JSON Path is the small expression language used by Phishlets 2.0 to point at one or more keys inside a JSON document. Wherever a field is documented as [json_path], the value is parsed with the rules described on this page.
In practice, JSON Path is used in two places:
- The
match_keyfield of a Locator whosescopeisbodyandformatisjson. - The
keyfield of arewriteaction that targets a value located via the above.
Syntax
A JSON Path is a sequence of segments joined by unescaped dots:
users.[*].profile.name
Each segment selects a key (or a set of keys) from the current level of the JSON document. Selectors are evaluated left to right, and a path can match many values when it contains wildcards, regexes, or array selectors.
Segments
| Segment | Meaning |
|---|---|
keyname | Matches the exact key keyname at the current level. |
*, something*, *.json | Glob pattern matched against the current level's keys. |
~/regex/ | Anchored regex match against the current level's keys (see Regex segments). |
[*] | All items of the current array. |
[N] | The item at index N of the current array (zero-based). |
[start:end] | A half-open slice [start, end) of the current array. |
[start:] | Items from start to the end of the array. |
[:end] | Items from the start of the array up to (but not including) end. |
[:] | All items of the array (equivalent to [*]). |
Escaping dots in key names
Because . is the segment separator, key names that contain a literal dot must escape it with a backslash:
config.settings\.override
This path navigates into config and then into the key literally named settings.override.
Regex segments
A segment of the form ~/pattern/ is compiled as a Go regular expression and automatically anchored with ^ and $. The pattern must match the entire key name:
users.[*].~/.+_id/
This matches every key under each users[i] that ends in _id (for example both user_id and guest_id).
To embed a literal / inside the pattern, escape it with \/. Dots inside a regex segment are not treated as path separators.
Self / parent / child references in rewrite.key
When a rewrite targets a value located via a JSON path, the action's key field may use one of these relative references:
| Reference | Meaning |
|---|---|
@ | The matched key itself (used by update / delete). |
@.child | A direct child of the matched value. |
@-1 | The parent of the matched key (go up one level). |
@-N.child | Go up N levels, then descend into child. |
Any value that does not start with @ is treated as an absolute path resolved from the document root.
Examples
The following examples reference this sample document:
{
"config": {
"version": "1.0",
"settings.override": true
},
"users": [
{
"user_id": 1,
"admin_id": 100,
"profile": {
"name": "Alice",
"preferences": {"theme": "dark"},
"enabled": true
}
},
{
"user_id": 2,
"guest_id": 200,
"profile": {
"name": "Bob",
"preferences": {"theme": "light"},
"enabled": true
}
},
{
"user_id": 3,
"guest_id": 300,
"profile": {
"name": "Sam",
"preferences": {"theme": "orange"},
"enabled": false
}
}
],
"items": [
{"id": "a", "value": 10},
{"id": "b", "value": 20}
]
}
Exact keys
config.version
Matches the string "1.0".
config.settings\.override
Matches the boolean true at the key literally named settings.override.
Globs
config.*
Matches both children of config (version and settings.override).
Array selectors
users.[*].profile.name
Matches "Alice", "Bob", and "Sam".
users.[2].profile.name
Matches "Sam" only (third array item).
users.[0:2].profile.name
Matches "Alice" and "Bob" (indices 0 and 1; the end bound is exclusive).
users.[1:].profile.name
Matches "Bob" and "Sam" (index 1 to end).
Regex on keys
users.[*].~/.+_id/
Matches user_id, admin_id, guest_id across all users — every key ending in _id.
Combining selectors
users.[*].profile.preferences.theme
Matches "dark", "light", and "orange".
Operations against matched paths
When a JSON Path is used in a rewrite, the action decides what happens to the matched key(s):
| Action | Effect |
|---|---|
update | Replace the value at the matched path. |
create | Create the key/value at the matched path only if the key does not already exist. |
delete | Remove the matched key(s) from the document. |
Combined with relative references in key, this lets rules act on neighbours of the matched value. For example, the M365 phishlet locates the authMethodId whose value is "FidoKey" and deletes the parent object so the entire MFA option disappears:
{
locator: {
scope: "body", format: "json",
match_key: 'arrUserProofs.[*].authMethodId',
match_value: 'FidoKey'
},
rewrite: { action: 'delete', key: '@-1' }
}
@-1 means "one level up from the matched key" — i.e. the array element that contained authMethodId.
Type preservation
When a value is replaced via update, the new value's textual form is coerced back to the original JSON type. Replacing a boolean true with the string "false" yields the boolean false, not the string "false". The same applies to integers (int64) and floats (float64). If the new string cannot be parsed as the original type, the operation returns an error.
See also
- String Matcher — syntax for
match_valueand other matcher fields. - Locator — where
match_keyis used. - Rewrite — actions that consume the matched key(s).