Skip to main content

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_key field of a Locator whose scope is body and format is json.
  • The key field of a rewrite action 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

SegmentMeaning
keynameMatches the exact key keyname at the current level.
*, something*, *.jsonGlob 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:

ReferenceMeaning
@The matched key itself (used by update / delete).
@.childA direct child of the matched value.
@-1The parent of the matched key (go up one level).
@-N.childGo 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):

ActionEffect
updateReplace the value at the matched path.
createCreate the key/value at the matched path only if the key does not already exist.
deleteRemove 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_value and other matcher fields.
  • Locator — where match_key is used.
  • Rewrite — actions that consume the matched key(s).