Appearance
Matchers and Patterns
Define which applications or files your policy targets by combining matcher types with clear, maintainable patterns. Matchers evaluate data discovered about an application — its shortcut or bundle, the resolved executable, and its signature — not arbitrary files on disk.
What You'll Learn
- Choose the right matcher for the job (shortcut, path, arguments, bundle, or high-assurance identity)
- Use pattern types (exact, regex, glob, semver) appropriately
- Author path rules with simple, readable globs
- Use
matchAll(AND),matchAny(OR) andmatchNone(exclusions) to build precise rules
Matcher Types
The matcher vocabulary is one list on every platform. A policy authored for Windows decodes on macOS and vice versa; a matcher type a platform cannot evaluate simply never matches there (the rule fails closed) and never refuses the document.
| Type | Description | Evaluated on | Example |
|---|---|---|---|
shortcutName | Display name of the shortcut (Windows) or application (macOS) | Windows, macOS | "Google Chrome" |
targetPath | Executable path; on macOS also the .app bundle path | Windows, macOS | "C:\Program Files\Google\Chrome\Application\chrome.exe", "**/Visual Studio Code.app" |
arguments | Shortcut command-line arguments | Windows only | "--incognito" |
publisherCertificate | Leaf code-signing certificate of the resolved executable: thumbprint, Subject CN (publisher name), Issuer CN, or full DN | Windows, macOS | "F0E1D2C3B4A5968778695A4B3C2D1E0FABCDEF12" or "Google LLC" |
fileHash | SHA-256 of the main executable's bytes | Windows, macOS | "9F2A2F2B…" |
version | Executable version (Windows: ProductVersion, then FileVersion; macOS: CFBundleShortVersionString) | Windows, macOS | "123.0.6422.141", ">=123.0" |
bundleId | macOS bundle identifier | macOS only | "com.microsoft.VSCode" |
teamId | Apple Developer Team identifier of the signer | macOS only | "UBF8T346G9" |
codeDirectoryHash | The signed bundle's code directory hash (cdhash); requires "schemaVersion": 2 | macOS only | "a1b2c3…" |
Notes
- On Windows the Launcher enumerates shortcut files (.lnk) from Start Menu and Desktop; on macOS it enumerates application bundles (LaunchServices, Homebrew) and command-line tools named by
targetPathmatchers. Executables must be in discovery scope to be matched and launched. teamIdandpublisherCertificateare the durable pins across updates;fileHashandcodeDirectoryHashpin one build and change on every re-sign (the macOS launcher re-signs the apps it stages).- An unknown
typestring — a typo, or a type introduced by a newer schema — also decodes as a matcher that never matches. The document is still applied; only that rule is affected.
Security Rule — Path vs. Identity
Path-based matching (targetPath, glob) validates location, not integrity. It is vulnerable to "Rename Attacks" where a user with write access to the target folder renames a malicious binary to match your allowed pattern.
Requirement: For high-assurance environments (Zero Trust, CMMC), you must combine path matchers with publisherCertificate or fileHash.
Pattern Types
| Type | Description |
|---|---|
exact | Exact text match (case-insensitive) |
regex | Regular expression, unanchored and case-insensitive |
glob | Shell-style wildcard matching for file paths (supports *, ?, and **). Easier to read and avoids regex escaping for typical path rules. See Glob patterns. |
semver | Version-range comparison for version matchers (>=, <=, ==, >, <). See version. |
Default when omitted
- The default depends on the document's
schemaVersion, on every reader: regex when the document declares version 1 (or nothing), glob when it declares version 2 or higher. Regex is unanchored, so the version-1 default matches anything containing the pattern — the over-authorizing reading. Always statepatternType. - An unknown
patternTypestring makes the matcher never match; the document is still applied.
Recommendations
- Always specify
patternTypeexplicitly to avoid ambiguity. - Thumbprints,
fileHashandcodeDirectoryHashvalues are always compared exactly and case-insensitively;patternTypeis ignored for them. Writeexactfor clarity.
File Type Extensions
- For fileTypes.match and handler-level match filters, author extensions using an
extensionsarray and avoid regex alternation for extensions. Matching is case-insensitive andpatternTypedoes not apply to theextensionsform.
json
{ "match": { "extensions": ["doc", "docx"] } }Single extension:
json
{ "match": { "extensions": ["pdf"] } }Prefer glob for Windows paths
Regex patterns in JSON require double escaping (for example: ".*\\myapp\.exe$"), which is cumbersome and error-prone. Use glob patterns instead (for example: "C:\\Program Files\\**\\myapp.exe") for simpler, more readable rules. Remember to escape backslashes in JSON strings.
Case Sensitivity
- exact
- Exact text match is case-insensitive across all matcher types. This includes publisher certificate thumbprints and SHA-256 hashes (hex is matched case-insensitively).
- regex
- Case-insensitive by default. This applies to Windows-targeted fields such as
shortcutNameandtargetPathso that "chrome.exe" and "Chrome.exe" both match. - For non-path fields (for example,
arguments,version, Subject/Issuer names), matching is also case-insensitive by default for consistency. If you need case-sensitive distinctions, author explicit character classes in your regex.
- Case-insensitive by default. This applies to Windows-targeted fields such as
- glob
- Intended for file paths (for example,
targetPath) and evaluated case-insensitively on both platforms, consistent with typical Windows path behavior.
- Intended for file paths (for example,
Notes
- File system paths are typically case-insensitive on Windows and on default macOS volumes; the readers align with this behavior for path-oriented fields regardless of pattern type.
Boolean Logic Semantics
Application policies support flexible logic using matchAll (AND) and matchAny (OR).
- Logical AND (
matchAll): All matchers in the array must evaluate to true. - Logical OR (
matchAny): At least one matcher in the array must evaluate to true. - Flat lists: a matcher list contains matchers only. Nested groups are not supported on any reader — a group object placed inside
matchAllormatchAnydecodes as a matcher of unknown type and never matches. Express (A OR B) AND C as two rules, or with regex alternation inside one field (below). - Rule: a rule carries either
matchAllormatchAny, not both. The JSON Schema rejects the pair; the Windows validator warns atschemaVersion1 and refuses at 2; the macOS daemon refuses. targetPathshorthand: a rule may instead name one executable directly with a rule-leveltargetPathstring. It is evaluated only when neithermatchAllnormatchAnyis populated, as a full-path, case-insensitive comparison after$VAR/${VAR}/%VAR%expansion, on every reader.- A rule with none of them matches nothing. There is no implied wildcard. Write a catch-all explicitly:
"matchAny": [{ "type": "targetPath", "pattern": "**", "patternType": "glob" }].
Exclusions (matchNone) A rule can carry a third matcher list, matchNone, holding exclusions. The rule applies when its positive side matches and no entry in matchNone matches. Any single entry matching vetoes the rule.
This is what expresses "authorize this broad set except these" — a catch-all over C:\Program Files\** that carves out three applications:
json
{
"matchAll": [
{ "type": "targetPath", "pattern": "C:\\\\Program Files\\\\**", "patternType": "glob" }
],
"matchNone": [
{ "type": "targetPath", "pattern": "**\\\\EXCEL.EXE", "patternType": "glob" },
{ "type": "targetPath", "pattern": "**\\\\POWERPNT.EXE", "patternType": "glob" },
{ "type": "shortcutName", "pattern": "Access", "patternType": "exact" }
]
}Requires "schemaVersion": 2. A document that uses matchNone without declaring it is refused, not accepted-and-ignored — see Version gate below.
Points worth knowing:
- It can only narrow. A rule is never matched by its positive side, excluded by
matchNone, and applied anyway. - It scopes one rule — it does not block an app. An excluded application is still authorized by any other rule that matches it. To say "this app is not permitted here", use an explicit deny; to say "this rule isn't about those", use
matchNone. See Authorization & Visibility. - It works on any positive form.
matchNonecomposes withmatchAll, withmatchAny, and with thetargetPathshorthand. - It never supplies the positive side. A rule with no
matchAll,matchAny, ortargetPathmatches nothing, with or withoutmatchNone— exclusions subtract from an empty set. Under"schemaVersion": 2such a rule is refused as an authoring error naming the rule; under version 1 it stays valid (a deployed document is not refused for a shape it already carried) and is reported as a dead rule. To say "everything except these", write the wildcard explicitly:"matchAny": [{ "type": "targetPath", "pattern": "**", "patternType": "glob" }]plusmatchNone. - An empty list changes nothing.
"matchNone": []andnullbehave exactly like an absent field and do not trip the version gate. - It is a rule-level list, not a per-matcher flag — deliberately. A negated matcher is only meaningful under AND; inside
matchAnyit could only widen the rule (NOT com.example.appis true of nearly every application), so it could not express an exclusion at all. - Every reader honours it: the Windows client, the macOS daemon and the macOS launcher all evaluate
matchNonefirst.
Version gatematchNone is the one field whose silent loss inverts a policy rather than degrading it: a reader that does not understand it reads "allow everything except X" as "allow everything, including X" — it authorizes precisely what you excluded. So the contract is strict in both directions:
- A policy using
matchNonemust declare"schemaVersion": 2. - A document declaring version 1 (or declaring nothing) that carries
matchNoneis refused, naming the offending rule. - A document declaring a version newer than a reader supports is refused rather than partially applied. The reader keeps its current policy.
Practically: publish matchNone only once every client in the estate understands it. Until then a v2 document is refused outright by older clients — which is the safe failure, but it is a refusal.
Other checks that tighten at version 2 Declaring "schemaVersion": 2 also turns two long-standing warnings into refusals, so a v2 document is held to the stricter reading on every platform:
- A rule carrying both
matchAllandmatchAnyis refused, naming the rule. (At version 1 it is a warning on Windows; the macOS daemon has always refused it.) - A rule with no
matchAll,matchAny, ortargetPathis refused, naming the rule (see It never supplies the positive side above). - An omitted
patternTypedefaults toglobat version 2 and to unanchoredregexat version 1, on every reader. Always statepatternType; the version-1 reader warns when it is missing.
Where matchNone does not fit — an exclusion about part of a single field's value rather than about which apps a rule covers — regex negative lookahead remains available, with the anchoring caveat noted in Limitations & Scope — Exclusions and negation. Prefer matchNone, and prefer positive, readable matches over both.
Other ways to express OR and (A OR B) AND C
- Author multiple allow policies when alternatives span different fields. Any matching allow policy authorizes. If more than one allow policy matches, merges resolve by priority then id. See: Merging & Precedence
- Use regex alternation within a single field when appropriate.
- For (A OR B) AND C, write one rule per alternative, each carrying C in its
matchAll— nested groups are not available.
Examples
AND (path + publisher) using matchAll:
json
{
"matchAll": [
{ "type": "targetPath", "pattern": "**\\\\myapp.exe", "patternType": "glob" },
{ "type": "publisherCertificate", "pattern": "5E0B1B36A6F3D1C2A6C9E4B1A46C9D18B1E3F2A469C7BA0E8F1A2B3C4D5E6F70", "patternType": "exact" }
]
}OR (simple list) using matchAny:
json
{
"matchAny": [
{ "type": "targetPath", "pattern": "**\\\\putty.exe", "patternType": "glob" },
{ "type": "targetPath", "pattern": "**\\\\psftp.exe", "patternType": "glob" },
{ "type": "targetPath", "pattern": "**\\\\pageant.exe", "patternType": "glob" },
{ "type": "targetPath", "pattern": "**\\\\puttygen.exe", "patternType": "glob" }
]
}(A OR B) AND C, as two rules:
json
[
{
"id": "allow-chrome-signed",
"matchAll": [
{ "type": "shortcutName", "pattern": "Google Chrome", "patternType": "exact" },
{ "type": "publisherCertificate", "pattern": "Google LLC", "patternType": "exact" }
]
},
{
"id": "allow-edge-signed",
"matchAll": [
{ "type": "shortcutName", "pattern": "Microsoft Edge", "patternType": "exact" },
{ "type": "publisherCertificate", "pattern": "Microsoft Corporation", "patternType": "exact" }
]
}
]Direct path via the targetPath shorthand (no matcher list):
json
{
"id": "system-tar",
"targetPath": "%SYSTEMROOT%\\System32\\cmd.exe",
"visibility": "hidden",
"action": "allow"
}OR (single field via regex alternation):
json
{
"matchAll": [
{ "type": "targetPath", "pattern": ".*\\\\(chrome|msedge)\\.exe$", "patternType": "regex" }
]
}NOT (exclude a specific argument via regex):
json
{
"matchAll": [
{ "type": "arguments", "pattern": "^(?!.*--private).*$", "patternType": "regex" }
]
}Glob patterns
One glob reading on every platform:
*matches any sequence within a single path segment (it does not cross/)?matches a single character within a single path segment**matches across path separators (recursive);**/may also match zero segments, so**/chrome.exematcheschrome.exeat any depth including the root- Matching is case-insensitive
\is normalised to/before matching, so a Windows pattern and a Windows path compare segment by segment whichever separator each used
Examples
C:\\\\Program Files\\\\*\\\\myapp.exe— exactly one folder betweenProgram Filesandmyapp.exeC:\\\\**\\\\Acrobat.exe**\\\\chrome.exe**/Visual Studio Code.app(macOS bundle path)
JSON escaping note
- When writing patterns inside JSON strings, Windows backslashes must be escaped as
\\.- Regex example:
".*\\\\myapp\\\.exe$" - Glob example:
"C:\\\\**\\\\myapp.exe"
- Regex example:
Windows paths in JSON require double backslashes
When writing Windows paths inside JSON strings, every \ in the actual path must be written as \\ in JSON.
Examples
- Correct glob:
"C:\\\\Program Files\\\\**\\\\myapp.exe" - Correct regex:
".*\\\\myapp\\\.exe$" - Incorrect:
"C:\\Program Files\MyApp\myapp.exe"(mixed or unescaped backslashes) — this either fails to parse or matches the wrong path.
Because JSON and regex escaping both apply to Windows paths, complex regex patterns quickly become hard to read and easy to get wrong. For path-based matchAll rules (for example, targetPath), prefer:
patternType: "glob"for most Windows path rules, and- simple, stable patterns such as
"C:\\\\Program Files\\\\**\\\\myapp.exe".
High-assurance matchers
Use these to bind identity and integrity, especially for sensitive applications.
publisherCertificate
Accepted fields
- Thumbprint (SHA-256 64-hex or SHA-1 40-hex; paste without spaces; case-insensitive)
- Subject CN (publisher display name, for example "Microsoft Corporation")
- Issuer CN (for example "Microsoft Windows Production PCA 2011")
Pattern guidance
- Use
patternType: "exact"for thumbprints and CNs (exactis case-insensitive). - Use
patternType: "regex"(orglob) against the full Subject or Issuer distinguished name to anchor on CN= specifically (for example:(^|,)\\s*CN=Microsoft Corporation(,|$)). - Without
field, the pattern is tried against the thumbprint, the Subject CN and the Issuer CN; a match on any of them matches. Statefieldwhen you mean one of them.
Trust behavior
- Unsigned executables do not match.
- For dual-signed binaries, matching is applied against the leaf signer used by platform trust (Windows Authenticode; macOS code signature).
Best practices
- Prefer Subject CN for maintainability; thumbprints rotate on renewals.
- Combine with
targetPathand/orversionfor higher assurance; addfileHashfor immutability.
Field selection (explicit "field" property)
- You can specify which certificate field to match with the optional field property on the publisherCertificate matcher:
- thumbprint — Leaf signer certificate thumbprint; 64-hex (SHA-256) or 40-hex (SHA-1). Always compared exactly;
patternTypeis ignored. - subjectCN — Leaf signer Subject Common Name (publisher display name); case-insensitive; prefer for maintainability.
- issuerCN — Issuer Common Name of the leaf signer’s issuing CA; use only when intentionally pinning to an issuer.
- subjectDN — Full Subject distinguished name (RFC 4514). Any pattern type; anchor explicitly on CN= to avoid ambiguity.
- issuerDN — Full Issuer distinguished name (RFC 4514). Any pattern type; anchor explicitly on CN= to avoid ambiguity.
- thumbprint — Leaf signer certificate thumbprint; 64-hex (SHA-256) or 40-hex (SHA-1). Always compared exactly;
Validation rules
- field: "thumbprint" → pattern should match ^(?:[A-Fa-f0-9]{64}|[A-Fa-f0-9]{40})$ (a non-hex thumbprint never matches).
- All certificate comparisons are case-insensitive.
Examples
Thumbprint pin: { "type": "publisherCertificate", "field": "thumbprint", "pattern": "5E0B1B36A6F3D1C2A6C9E4B1A46C9D18B1E3F2A469C7BA0E8F1A2B3C4D5E6F70", "patternType": "exact" }
Subject CN: { "type": "publisherCertificate", "field": "subjectCN", "pattern": "Microsoft Corporation", "patternType": "exact" }
Issuer CN: { "type": "publisherCertificate", "field": "issuerCN", "pattern": "Microsoft Windows Production PCA 2011", "patternType": "exact" }
Subject DN (regex anchored on CN): { "type": "publisherCertificate", "field": "subjectDN", "pattern": "(^|,)\s*CN=Microsoft Corporation(,|$)", "patternType": "regex" }
fileHash
- Matches the SHA-256 hash of the resolved executable (on macOS, the main Mach-O only — frameworks and resources are outside it, and the digest changes on every re-sign).
- Strongest integrity guarantee; any update requires a policy update.
- Always compared exactly and case-insensitively;
patternTypeis ignored. - Hashing is performed only when a
fileHashmatcher is present or when cache is invalidated.
codeDirectoryHash (macOS)
- Matches the code directory hash (
cdhash) of the signed bundle — whatcodesign -dvvvprints asCDHash. It seals every resource in the bundle, so it pins one exact build. Requires"schemaVersion": 2; never matches on Windows. - Always compared exactly;
patternTypeis ignored.
version
- Matches ProductVersion (fallback to FileVersion) from PE metadata on Windows, and
CFBundleShortVersionStringon macOS. patternTypedecides the comparison, on every reader:semver— a range compare with one operator:>=1.2,<2.0,==1.2.3,>1.0,<=1.9. Numeric parts compare numerically with missing parts read as zero, so>=1.2accepts1.2.0and1.10. WhenpatternTypeis omitted and the pattern starts with an operator, it is read assemver.exact— pins one version string (case-insensitive).regex/glob— a string match on the version text (for example^(1\\.(2|3)\\.[0-9]+)$for 1.2.x–1.3.x).
- There is no
1.xwildcard form; anxin a pattern is a literalx. Usesemverbounds or a regex.
Enterprise recommendations
- Bind identity to location:
targetPath+publisherCertificate(orteamIdon macOS). - Constrain updates: add a
versionmatcher (semverbounds, or a regex for a range). - Require immutability:
publisherCertificate+fileHash(operationally heavy).
Obtaining values
PowerShell snippets to extract values from an executable:
Publisher certificate thumbprint: (Get-AuthenticodeSignature 'C:\Path\To\app.exe').SignerCertificate.Thumbprint
SHA-256 file hash: (Get-FileHash -Algorithm SHA256 'C:\Path\To\app.exe').Hash
Version (ProductVersion): (Get-Item 'C:\Path\To\app.exe').VersionInfo.ProductVersion
Examples
Pin by publisher and version (glob path):
json
{
"matchAll": [
{ "type": "targetPath", "pattern": "**\\\\myapp.exe", "patternType": "glob" },
{ "type": "publisherCertificate", "pattern": "A1B2C3D4E5F60718273645546352413F0E1D2C3B", "patternType": "exact" },
{ "type": "version", "pattern": "^(1\\.(2|3)\\.[0-9]+)$", "patternType": "regex" }
]
}Allow all Program Files executables (glob path):
json
{
"matchAll": [
{ "type": "targetPath", "pattern": "C:\\\\Program Files\\\\**\\\\*.exe", "patternType": "glob" }
]
}Network destination matching
Use these patterns to match the destination of a network connection for proxy routing or egress policies. Host matching is case-insensitive; prefer glob for readability.
Supported match fields
- hosts: Array of HostPatternSpec objects (pattern + patternType). Case-insensitive. Prefer glob (e.g., "*.corp.local").
- cidrs: Array of IPv4/IPv6 CIDR strings (e.g., "10.0.0.0/8", "2001:db8::/32").
- ipRanges: Array of { from, to } with IPv4/IPv6 addresses.
- ports: Array of PortSpec strings (single "443" or range "8080-8090").
- protocol: "tcp" | "udp" | "icmp".
HostPatternSpec
json
{ "pattern": "*.corp.local", "patternType": "glob" }CIDR examples
- "10.0.0.0/8"
- "172.16.0.0/12"
- "192.168.0.0/16"
- "2001:db8::/32"
IP range example
json
{ "from": "1.1.1.1", "to": "1.1.1.255" }PortSpec
- Single: "443"
- Range: "10000-10100"
- Valid ports are 0–65535; ranges must be start ≤ end.
Examples
Hosts via glob (proxy internet, deny otherwise)
json
{
"match": { "hosts": [ { "pattern": "*", "patternType": "glob" } ], "protocol": "tcp" }
}CIDR-based direct for RFC1918
json
{
"match": { "cidrs": ["10.0.0.0/8","172.16.0.0/12","192.168.0.0/16"] }
}IP range + port
json
{
"match": { "ipRanges": [ { "from": "203.0.113.0", "to": "203.0.113.255" } ], "ports": ["443"], "protocol": "tcp" }
}Guidance
- Prefer CIDR for known networks and host globs for domains; use regex only for complex host rules.
- Hosts are matched case-insensitively. CIDR/range matching applies equally to IPv4 and IPv6.
- Combine filters (e.g., host + port + protocol) to narrow matches.
Next steps
- Authorization and visibility: Authorization & Visibility
- Merging and precedence: Merging & Precedence
- File handling: File Handling
