From ce6fd457b70bae3017d6cfbb578418fe22413d27 Mon Sep 17 00:00:00 2001 From: Maxime Lamothe-Brassard Date: Sun, 4 Oct 2026 03:21:15 +0000 Subject: [PATCH 1/3] Document Application Control Add the Application Control extension page, Config Hive pages for the app_control_policy and app_control_rule hives, the APP_CONTROL_DENIED and APP_CONTROL_UNRESOLVED events, and the app_control.get / app_control.set permissions. Link them from the nav and the index pages. Co-Authored-By: Claude Sonnet 5.5 --- .../extensions/limacharlie/app-control.md | 152 ++++++++++++++ .../extensions/limacharlie/index.md | 1 + .../config-hive/app-control-policy.md | 186 ++++++++++++++++++ .../config-hive/app-control-rule.md | 184 +++++++++++++++++ docs/7-administration/config-hive/index.md | 4 + docs/8-reference/edr-events.md | 28 +++ docs/8-reference/permissions.md | 7 + mkdocs.yml | 3 + 8 files changed, 565 insertions(+) create mode 100644 docs/5-integrations/extensions/limacharlie/app-control.md create mode 100644 docs/7-administration/config-hive/app-control-policy.md create mode 100644 docs/7-administration/config-hive/app-control-rule.md diff --git a/docs/5-integrations/extensions/limacharlie/app-control.md b/docs/5-integrations/extensions/limacharlie/app-control.md new file mode 100644 index 000000000..7fda77c9e --- /dev/null +++ b/docs/5-integrations/extensions/limacharlie/app-control.md @@ -0,0 +1,152 @@ +# Application Control + +## Overview + +Application Control lets you decide which programs may run on your endpoints. You write an allowlist (only approved software runs) or a blocklist (everything runs except what you deny), and the LimaCharlie sensor enforces it at the moment a process starts. + +- Windows and macOS are supported. Linux is not. +- The policy is declarative. You describe the desired state in two [Config Hive](../../../7-administration/config-hive/index.md) types, and the extension reconciles it onto every matching sensor each time that sensor syncs. +- The LimaCharlie cloud signs each policy before it reaches a sensor, and the sensor verifies the signature. +- Each policy has a mode, so you can watch what would be blocked before you block anything. + +## Requirements + +- The `ext-app-control` extension subscribed in the organization. See [Enabling the extension](#enabling-the-extension). +- Endpoint agent **5.4.0** or later. [Upgrade](../../../2-sensors-deployment/endpoint-agent/versioning-upgrades.md) older sensors first. +- Windows or macOS. +- The `app_control.get` and `app_control.set` [permissions](../../../8-reference/permissions.md#application-control) to read and write policies. + +## Enabling the extension + +Open the Application Control page in the Add-Ons marketplace, choose the organization and select **Subscribe**. + +Subscribing installs a managed D&R rule named `ext-app-control-sync`. That rule reconciles policy onto each sensor every time it syncs. Leave it in place. The extension re-creates it if it drifts and removes it when you unsubscribe. + +!!! note + Subscribing changes nothing on its own. A sensor is left untouched until a policy matches it. + +## How it works + +Two hives hold the configuration. Both are partitioned by organization. + +| Hive | One record is | Page | +| --- | --- | --- | +| `app_control_policy` | A policy. It says which sensors it covers (by platform and tag), the mode, the stance (allowlist or blocklist) and whether to trust the OS vendor. | [Policies](../../../7-administration/config-hive/app-control-policy.md) | +| `app_control_rule` | A rule. It allows or denies one path, signer, signing identifier, certificate-chain thumbprint or file hash, for all policies or only the ones you name. | [Rules](../../../7-administration/config-hive/app-control-rule.md) | + +A sensor gets the first enabled policy, ordered by `priority` and then by name, whose platform and tags match it. When the sensor starts a process, it checks the rules that apply to its policy in this order and stops at the first answer: + +1. Deny rules. +2. Allow rules. +3. OS vendor trust (Apple platform binaries, Microsoft-signed binaries), unless the policy turns it off. +4. The stance. An allowlist denies anything not allowed. A blocklist allows anything not denied. + +A deny rule always wins. + +### Modes + +| Mode | What the sensor does | +| --- | --- | +| `off` | Evaluates nothing. | +| `permissive` | Evaluates executions asynchronously after the process starts. Reports would-be blocks. Blocks nothing and adds no latency. | +| `permissive_sync` | Runs the same blocking path as `enforcing`, then always allows. The recommended last step before enforcing. | +| `enforcing` | Blocks executions the policy denies. | + +## Rolling out + +Start in a mode that cannot block and move forward only once the reports are quiet. Tags make this easy because a policy can target a tag, and moving a sensor between stages is a tag change. The [policy page](../../../7-administration/config-hive/app-control-policy.md#staged-rollout-by-tag) has the three policies for this flow. + +1. **Observe in `permissive`.** Create an allowlist policy for the platform with no tag filter. Add the rules you already know you need (your software publishers, your standard install locations). Every execution that the policy would deny shows up as an `APP_CONTROL_DENIED` event with `APP_CONTROL_IS_ENFORCED` false. Nothing is blocked and process start is not slowed. +2. **Fix the rules.** Read the would-be blocks (see [Reading would-be blocks](#reading-would-be-blocks)). For each legitimate program, add an allow rule. Prefer a `signer` rule for software that updates, and use a `path` rule only for locations ordinary users cannot write to. Repeat until the legitimate noise is gone. Use a [temporary exception](../../../7-administration/config-hive/app-control-rule.md#a-temporary-exception) for one-off cases. +3. **Soak a pilot in `permissive_sync`.** Add a policy that targets a pilot tag, such as `app-control-soak`, in `permissive_sync`. These sensors run the full blocking path but still allow everything. This is the last chance to find a problem before blocking. +4. **Enforce the pilot.** Add a policy with a lower priority number than the other two that targets `app-control-enforce` in `enforcing`. Tag a small group of machines and watch them. +5. **Widen.** Tag more machines. Keep a broad `permissive` policy at the end of the order so that untagged machines keep reporting. + +To step back at any point, remove the tag, or set the policy to `permissive` or `off`. The change reaches sensors on their next sync. + +!!! warning "Deleting does not disarm" + Removing a policy, or unsubscribing from the extension, does not disarm sensors that already hold a policy. They keep enforcing it. To stand enforcement down, set the policy to `mode: off` and let sensors sync before you remove anything. + +!!! note + An `enforcing` allowlist with `trust_os_vendor: false` is refused on save, because a sensor cannot apply it. + +## Reading would-be blocks + +Application Control reports through two events, available on Windows and macOS. See the [EDR events reference](../../../8-reference/edr-events.md#app_control_denied) for the full fields. + +`APP_CONTROL_DENIED` means the policy denied an execution. If `APP_CONTROL_IS_ENFORCED` is true, the sensor blocked it. If it is false, the sensor is in `permissive` or `permissive_sync` and only reports what it would have blocked. + +`APP_CONTROL_UNRESOLVED` means the sensor could not evaluate an execution and allowed it. + +Useful fields on `APP_CONTROL_DENIED`: + +| Field | Meaning | +| --- | --- | +| `FILE_PATH` | The program that was denied. | +| `HASH` | The file's SHA-256, when available. | +| `APP_CONTROL_SIGNER` | The signer the sensor saw. | +| `APP_CONTROL_SIGNING_ID` | The macOS code-signing identifier the sensor saw. | +| `APP_CONTROL_REASON` | Why the sensor reached the decision. | +| `APP_CONTROL_MATCHED_RULE` | Optional. The rule that matched, when there is one. | +| `APP_CONTROL_MODE` | The mode of the policy in effect. | +| `APP_CONTROL_GENERATION` | The generation of the policy the sensor was running. | + +To turn would-be blocks into something you can list and count, write a D&R rule that reports them: + +```yaml +detect: + event: APP_CONTROL_DENIED + op: is + path: event/APP_CONTROL_IS_ENFORCED + value: false +respond: + - action: report + name: app-control-would-block +``` + +Group the resulting detections by `FILE_PATH` or signer to see which programs matter most. A signer that appears on many machines is a candidate for a `signer` allow rule. A path seen on one machine is usually a one-off. To alert on actual blocks instead, match `true` and change the report name. + +Watch `APP_CONTROL_UNRESOLVED` during the soak steps. Each one is an execution the sensor let through because it could not decide, so it is a gap in what the policy covers. + +## Managing from the CLI + +The two hives work with the generic hive commands. + +```bash +# List the policies and rules. +limacharlie hive list --hive-name app_control_policy +limacharlie hive list --hive-name app_control_rule + +# Create or update a rule. +limacharlie hive set \ + --hive-name app_control_rule \ + --key allow-program-files \ + --input-file rule.json \ + --enabled +``` + +Examples of both record types are on the [policy](../../../7-administration/config-hive/app-control-policy.md#examples) and [rule](../../../7-administration/config-hive/app-control-rule.md#examples) pages. + +## Permissions + +| Permission | Allows | +| --- | --- | +| `app_control.get` | Read policies and rules. | +| `app_control.set` | Create, edit and delete policies and rules, and their metadata. | + +By default the Owner, Administrator and Operator roles have both. The Viewer role has `app_control.get`. + +## Limits + +- 10,000 rules may apply to a single policy. +- Policy record names are limited to 256 bytes and rule ids to 64 bytes. +- A policy can list at most 64 tags. + +## See Also + +- [Application Control policies](../../../7-administration/config-hive/app-control-policy.md) +- [Application Control rules](../../../7-administration/config-hive/app-control-rule.md) +- [Sensor tags](../../../2-sensors-deployment/sensor-tags.md) +- [Config Hive overview](../../../7-administration/config-hive/index.md) +- [Permissions](../../../8-reference/permissions.md#application-control) +- [EDR events reference](../../../8-reference/edr-events.md#app_control_denied) diff --git a/docs/5-integrations/extensions/limacharlie/index.md b/docs/5-integrations/extensions/limacharlie/index.md index 0fdb09b13..70fa16e6e 100644 --- a/docs/5-integrations/extensions/limacharlie/index.md +++ b/docs/5-integrations/extensions/limacharlie/index.md @@ -4,6 +4,7 @@ Extensions built and maintained by LimaCharlie that extend the platform with add ## Available Extensions +- [Application Control](app-control.md) - Application allowlisting and blocklisting on Windows and macOS - [Artifact](artifact.md) - Collect and store forensic artifacts - [BinLib](binlib.md) - Binary library management - [Cases](cases.md) - Case management for investigations diff --git a/docs/7-administration/config-hive/app-control-policy.md b/docs/7-administration/config-hive/app-control-policy.md new file mode 100644 index 000000000..8c1a909b1 --- /dev/null +++ b/docs/7-administration/config-hive/app-control-policy.md @@ -0,0 +1,186 @@ +# Config Hive: Application Control Policies + +The `app_control_policy` hive holds the policies of [Application Control](../../5-integrations/extensions/limacharlie/app-control.md). A policy decides which sensors it covers, whether it blocks or only reports, and what the default answer is for a program that no rule mentions. The rules themselves live in the [`app_control_rule`](app-control-rule.md) hive. + +Both hives are partitioned by organization, like the other Config Hive types. Each policy is one record. The record name is the policy name, up to 256 bytes, and rules refer to it by that name. + +## Format + +```json +{ + "priority": 10, + "platforms": ["windows"], + "tags": ["app-control-enforce"], + "mode": "enforcing", + "stance": "allowlist", + "trust_os_vendor": true +} +``` + +| Field | Required | Description | +| --- | --- | --- | +| `priority` | No | Integer, default `0`. A lower number is evaluated first. Policies with the same priority are ordered by record name, ascending. | +| `platforms` | No | List of `windows` and `macos`. A sensor matches if it runs any one of them. Empty or omitted means every supported platform. | +| `tags` | No | List of sensor tags. A sensor matches only if it carries all of them. Empty or omitted matches any sensor. At most 64 tags, each at most 256 bytes, with no duplicates and no leading or trailing whitespace. | +| `mode` | Yes | `off`, `permissive`, `permissive_sync` or `enforcing`. See [Modes](#modes). | +| `stance` | Yes | `allowlist` or `blocklist`. There is no default, so you always choose one on purpose. See [Stance](#stance). | +| `trust_os_vendor` | No | Boolean, default `true`. When `true`, binaries signed by the operating system vendor are implicitly allowed: Apple platform binaries on macOS and Microsoft-signed binaries on Windows. Deny rules still win. | + +Keywords are case-sensitive and must be lowercase exactly as shown. Unknown fields are refused when you save the record. + +!!! warning "A locked-out allowlist is refused" + An `enforcing` policy with stance `allowlist` and `trust_os_vendor: false` is refused on save. A sensor cannot apply it, because it would block the operating system itself. + +## Which policy a sensor gets + +A sensor receives one policy. LimaCharlie takes the enabled policies, orders them by `priority` and then by record name, and gives the sensor the first one whose `platforms` and `tags` both match. Policies later in the order are ignored for that sensor. + +- A sensor that matches no policy is left alone. +- A policy with no `platforms` and no `tags` matches every Windows and macOS sensor. Give it the highest `priority` number so that narrower policies are checked first. +- Disabled records are skipped. + +Because the first match wins, you stage a rollout by putting narrow policies (a pilot tag) ahead of a broad one. See [Staged rollout by tag](#staged-rollout-by-tag). + +## Modes + +| Mode | Behavior | +| --- | --- | +| `off` | The sensor evaluates nothing. | +| `permissive` | The sensor evaluates each execution asynchronously, after the process has started. It reports would-be blocks and blocks nothing. It adds no latency to process start. | +| `permissive_sync` | The sensor runs the same blocking path as `enforcing`, then always allows. Nothing is blocked. Use this as the last soak step before enforcing. | +| `enforcing` | The sensor blocks executions that the policy denies. | + +In `permissive` and `permissive_sync`, a would-be block is reported as an `APP_CONTROL_DENIED` event with `APP_CONTROL_IS_ENFORCED` set to false. See [Reading would-be blocks](../../5-integrations/extensions/limacharlie/app-control.md#reading-would-be-blocks). + +## Stance + +The stance sets the answer for a program that no rule matches. + +- `allowlist` denies anything not explicitly allowed. This is the strict model. Plan on an inventory phase before you enforce it. +- `blocklist` allows anything not explicitly denied. Under a blocklist an `allow` rule never changes an outcome, because the default is already allow. Use `allow` rules in a blocklist policy only if you intend to switch the stance later. + +The sensor works through the policy in a fixed order and stops at the first answer: + +1. Deny rules. A matching deny rule always blocks. +2. Allow rules. +3. OS vendor trust, if `trust_os_vendor` is `true`. +4. The stance. + +## Permissions + +Managing records in the `app_control_policy` hive requires: + +- `app_control.get` to read policies. +- `app_control.set` to create, edit and delete policies and their metadata. + +See [Permissions](../../8-reference/permissions.md#application-control). + +## Examples + +All examples use the CLI generic hive commands. Pass `--oid ` if your CLI is not already pointed at the organization. Records created without metadata are enabled by default in this hive, and the examples pass `--enabled` anyway so the intent is visible. + +### A Windows allowlist policy + +Save this as `windows-allowlist.json`. It covers every Windows sensor, evaluates but does not block, and trusts Microsoft-signed binaries. + +```json +{ + "priority": 100, + "platforms": ["windows"], + "mode": "permissive", + "stance": "allowlist", + "trust_os_vendor": true +} +``` + +```bash +limacharlie hive set \ + --hive-name app_control_policy \ + --key windows-allowlist \ + --input-file windows-allowlist.json \ + --enabled +``` + +Rules with an empty `policies` list apply to this policy, as do rules that name `windows-allowlist`. The [rule page](app-control-rule.md#examples) has a matching rule set. + +Validate a policy before you save it: + +```bash +limacharlie hive validate \ + --hive-name app_control_policy \ + --key windows-allowlist \ + --input-file windows-allowlist.json +``` + +### Staged rollout by tag + +Three policies move sensors through the rollout by tag. A sensor that carries `app-control-enforce` is blocked on violations. A sensor with `app-control-soak` runs the full blocking path without blocking. Every other Windows sensor only reports. + +```json +{ + "priority": 10, + "tags": ["app-control-enforce"], + "platforms": ["windows"], + "mode": "enforcing", + "stance": "allowlist", + "trust_os_vendor": true +} +``` + +Saved under the key `windows-1-enforce`. + +```json +{ + "priority": 20, + "tags": ["app-control-soak"], + "platforms": ["windows"], + "mode": "permissive_sync", + "stance": "allowlist", + "trust_os_vendor": true +} +``` + +Saved under the key `windows-2-soak`. + +```json +{ + "priority": 100, + "platforms": ["windows"], + "mode": "permissive", + "stance": "allowlist", + "trust_os_vendor": true +} +``` + +Saved under the key `windows-3-observe`. + +Priority 10 is checked first, so a sensor tagged both `app-control-enforce` and `app-control-soak` is enforcing. To promote a machine, add the next tag and remove the old one using [sensor tags](../../2-sensors-deployment/sensor-tags.md). The change reaches the sensor on its next sync. + +The record names here are only labels. The `priority` values decide the order, and the names only break ties. + +### Standing down + +Deleting a policy, or unsubscribing from the extension, does not disarm sensors that already hold a policy. They keep enforcing what they last received. To stand enforcement down, set the policy to `mode: off` and let sensors sync before you delete anything. + +```bash +limacharlie hive set \ + --hive-name app_control_policy \ + --key windows-1-enforce \ + --input-file windows-off.json \ + --enabled +``` + +where `windows-off.json` is the same policy with `"mode": "off"`. + +## Limits + +- Record name: 256 bytes. +- Tags per policy: 64, each at most 256 bytes. +- Rules that apply to a single policy: 10,000. + +## See Also + +- [Application Control](../../5-integrations/extensions/limacharlie/app-control.md) +- [Application Control rules](app-control-rule.md) +- [Sensor tags](../../2-sensors-deployment/sensor-tags.md) +- [Config Hive overview](index.md) diff --git a/docs/7-administration/config-hive/app-control-rule.md b/docs/7-administration/config-hive/app-control-rule.md new file mode 100644 index 000000000..09e4931d0 --- /dev/null +++ b/docs/7-administration/config-hive/app-control-rule.md @@ -0,0 +1,184 @@ +# Config Hive: Application Control Rules + +The `app_control_rule` hive holds the allow and deny rules of [Application Control](../../5-integrations/extensions/limacharlie/app-control.md). Each rule is one record. The record name is the rule id, up to 64 bytes. A rule says what to match (a path, a signer, a file hash) and whether to allow or deny it, and it can be limited to specific [policies](app-control-policy.md). + +Like the policy hive, `app_control_rule` is partitioned by organization. + +## Format + +```json +{ + "action": "allow", + "kind": "path", + "value": "C:\\Program Files\\", + "policies": ["windows-allowlist"] +} +``` + +| Field | Required | Description | +| --- | --- | --- | +| `action` | Yes | `allow` or `deny`. | +| `kind` | Yes | What `value` matches. One of `path`, `signer`, `signing_id`, `signer_root`, `sha256`. See [Rule kinds](#rule-kinds). | +| `value` | Yes | The value to match. No leading or trailing whitespace, at most 1024 bytes. | +| `policies` | No | List of `app_control_policy` record names the rule applies to. Empty or omitted means the rule applies to every policy. At most 64 entries, with no duplicates. | + +Keywords are case-sensitive and must be lowercase exactly as shown. Unknown fields are refused when you save the record. + +Two things live in the record's metadata rather than in its data: + +- A comment explaining why the rule exists goes in the metadata `comment`. +- A temporary exception takes a metadata `expiry`. See [A temporary exception](#a-temporary-exception). + +Only enabled records apply. Records created without metadata are enabled by default in this hive. + +## Rule kinds + +| Kind | Matches | Platforms | +| --- | --- | --- | +| `path` | An exact file path. If the value ends with `\` or `/`, it matches every file under that directory instead. | Windows, macOS | +| `signer` | The Authenticode subject of the signing certificate on Windows, or the Apple Team ID on macOS. | Windows, macOS | +| `signing_id` | The macOS code-signing identifier. | macOS | +| `signer_root` | The SHA-256 thumbprint of any certificate in the validated certificate chain. | Windows | +| `sha256` | The SHA-256 hash of the file, as 64 hex characters. | Windows, macOS | + +Notes on each kind: + +- **`path`.** There are no wildcards. `*` and `?` are literal characters. Matching is case-insensitive for ASCII characters. +- **`signer_root`.** Use the 64-character SHA-256 thumbprint. Windows shows the 40-character SHA-1 thumbprint by default, and that value does not match. The rule matches if any certificate in the chain, root or intermediate, has the thumbprint. +- **`sha256`.** A hash rule pins one exact build of one file. It stops matching when the vendor ships an update, so it suits denying a known bad file better than allowing software that updates. + +A path rule that allows a directory also allows whatever a user can write into it. Prefer signer rules for software that installs somewhere users can write, and keep path allows to locations that only administrators can change. + +## How rules are evaluated + +The sensor evaluates the rules that apply to the sensor's policy in this order and stops at the first answer: + +1. Deny rules. If any deny rule matches, the execution is denied. +2. Allow rules. +3. OS vendor trust, if the policy has `trust_os_vendor: true`. +4. The policy stance. An `allowlist` denies anything not allowed. A `blocklist` allows anything not denied. + +So a deny rule always wins over an allow rule, whatever their order or names. Under a `blocklist`, an allow rule never changes an outcome. + +At most 10,000 rules may apply to a single policy. A rule with an empty `policies` list counts toward every policy. + +## Permissions + +Managing records in the `app_control_rule` hive requires: + +- `app_control.get` to read rules. +- `app_control.set` to create, edit and delete rules and their metadata. + +See [Permissions](../../8-reference/permissions.md#application-control). + +## Examples + +The examples use the CLI generic hive commands. Pass `--oid ` if your CLI is not already pointed at the organization. They build on the `windows-allowlist` policy from the [policy page](app-control-policy.md#a-windows-allowlist-policy). + +### An allow rule for a directory + +Save this as `rule.json`. The trailing backslash makes it a directory prefix. In JSON, each backslash is written twice. + +```json +{ + "action": "allow", + "kind": "path", + "value": "C:\\Program Files\\", + "policies": ["windows-allowlist"] +} +``` + +```bash +limacharlie hive set \ + --hive-name app_control_rule \ + --key allow-program-files \ + --input-file rule.json \ + --enabled \ + --comment "Software installed by administrators" +``` + +### An allow rule for a signer + +This allows everything signed by a company. The `value` is the Authenticode subject of the signing certificate. + +```json +{ + "action": "allow", + "kind": "signer", + "value": "C=US, S=California, L=San Francisco, O=Example Corp, CN=Example Corp", + "policies": ["windows-allowlist"] +} +``` + +Take the exact string from a real execution rather than typing it. The `APP_CONTROL_SIGNER` field of an `APP_CONTROL_DENIED` event shows the signer the sensor saw. See [Reading would-be blocks](../../5-integrations/extensions/limacharlie/app-control.md#reading-would-be-blocks). + +### A deny rule that overrides allows + +Because deny rules are evaluated first, this blocks a user-writable location even though other rules allow broad areas. + +```json +{ + "action": "deny", + "kind": "path", + "value": "C:\\Users\\Public\\" +} +``` + +With no `policies` list it applies to every policy. + +### Denying a known file by hash + +```json +{ + "action": "deny", + "kind": "sha256", + "value": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" +} +``` + +The value above is a placeholder. Use the real SHA-256 of the file. + +### A temporary exception + +Give a vendor installer a time-limited allow. Set the expiry with the CLI `--expiry` flag, which takes Unix epoch **seconds**. + +```json +{ + "action": "allow", + "kind": "path", + "value": "C:\\Temp\\vendor-setup.exe", + "policies": ["windows-allowlist"] +} +``` + +```bash +limacharlie hive set \ + --hive-name app_control_rule \ + --key temp-allow-vendor-setup \ + --input-file temp-rule.json \ + --enabled \ + --expiry 1793491200 \ + --comment "Vendor installer, approved for the November maintenance window" +``` + +`1793491200` is 2026-11-01 00:00:00 UTC. The hive stores expiry in milliseconds, so `hive get` shows `1793491200000`. If you write the record with a `usr_mtd` block instead of the flag, give the expiry in milliseconds there. + +### Listing and reading rules + +```bash +limacharlie hive list --hive-name app_control_rule +limacharlie hive get --hive-name app_control_rule --key allow-program-files +``` + +## Limits + +- Record name (the rule id): 64 bytes. +- `value`: 1024 bytes. +- `policies` entries per rule: 64. +- Rules applying to a single policy: 10,000. + +## See Also + +- [Application Control](../../5-integrations/extensions/limacharlie/app-control.md) +- [Application Control policies](app-control-policy.md) +- [Config Hive overview](index.md) diff --git a/docs/7-administration/config-hive/index.md b/docs/7-administration/config-hive/index.md index 342a962cb..6d166f906 100644 --- a/docs/7-administration/config-hive/index.md +++ b/docs/7-administration/config-hive/index.md @@ -9,6 +9,8 @@ The Config Hive is LimaCharlie's hierarchical configuration store. It provides a - [Secrets](secrets.md) - Secure credential management - [YARA](yara.md) - YARA rule storage and management - [Cloud Sensors](cloud-sensors.md) - Cloud sensor configurations +- [Application Control Policies](app-control-policy.md) - Which sensors Application Control covers, and in which mode +- [Application Control Rules](app-control-rule.md) - Allow and deny rules for Application Control - [Apps](apps.md) - User-authored, AI-generated mini web applications - [SOPs](../../9-ai-sessions/sops.md) - Standard Operating Procedures that AI agents read and follow - [Organization Notes](../../9-ai-sessions/org-notes.md) - Free-form reference documents about the organization, read by analysts and AI agents @@ -43,3 +45,5 @@ Hive records can be: - [D&R Rules](dr-rules.md) - [Secrets Manager](secrets.md) - [Lookups](lookups.md) +- [Application Control Policies](app-control-policy.md) +- [Application Control Rules](app-control-rule.md) diff --git a/docs/8-reference/edr-events.md b/docs/8-reference/edr-events.md index 117068226..9f7fdcc03 100644 --- a/docs/8-reference/edr-events.md +++ b/docs/8-reference/edr-events.md @@ -12,6 +12,8 @@ These are the events emitted by the endpoint agent for each supported operating | EDR Event Type | macOS | Windows | Linux | Chrome | Edge | | --- | --- | --- | --- | --- | --- | +| [APP\_CONTROL\_DENIED](#app_control_denied) | ☑️ | ☑️ | | | | +| [APP\_CONTROL\_UNRESOLVED](#app_control_unresolved) | ☑️ | ☑️ | | | | | [AUTORUN\_CHANGE](#autorun_change) | | ☑️ | | | | | [CLOUD\_NOTIFICATION](#cloud_notification) | ☑️ | ☑️ | ☑️ | ☑️ | ☑️ | | [CODE\_IDENTITY](#code_identity) | ☑️ | ☑️ | ☑️ | | | @@ -115,6 +117,32 @@ These are the events emitted by the endpoint agent for each supported operating ## Event Descriptions +### APP\_CONTROL\_DENIED + +Generated by [Application Control](../5-integrations/extensions/limacharlie/app-control.md) when the policy denies an execution. `APP_CONTROL_IS_ENFORCED` tells you whether the sensor blocked it (`true`) or, in a `permissive` or `permissive_sync` policy, only reported a would-be block (`false`). + +| Field | Description | +| --- | --- | +| `FILE_PATH` | Path of the program. | +| `PROCESS_ID` | Process ID of the execution. | +| `HASH` | SHA-256 of the file. Optional. | +| `APP_CONTROL_DECISION` | The decision the sensor reached. | +| `APP_CONTROL_REASON` | The reason for the decision. | +| `APP_CONTROL_MODE` | Mode of the policy in effect. | +| `APP_CONTROL_GENERATION` | Generation of the policy the sensor was running. | +| `APP_CONTROL_IS_ENFORCED` | Whether the denial was enforced. | +| `APP_CONTROL_MATCHED_RULE` | The rule that matched. Optional. | +| `APP_CONTROL_SIGNER` | The signer the sensor saw. | +| `APP_CONTROL_SIGNING_ID` | The code-signing identifier the sensor saw (macOS). | + +Available on Windows and macOS. + +### APP\_CONTROL\_UNRESOLVED + +Generated by [Application Control](../5-integrations/extensions/limacharlie/app-control.md) when the sensor could not evaluate an execution and allowed it. Its fields are the ones listed for `APP_CONTROL_DENIED`, and the optional ones may be absent. + +Available on Windows and macOS. + ### AUTORUN\_CHANGE Generated when an Autorun is changed. diff --git a/docs/8-reference/permissions.md b/docs/8-reference/permissions.md index 4180ec7b4..df2c18eb7 100644 --- a/docs/8-reference/permissions.md +++ b/docs/8-reference/permissions.md @@ -282,6 +282,13 @@ approval; they do not grant organization API permissions. See | app.get.mtd | View app metadata only | | app.set.mtd | Modify app metadata only | +### Application Control + +| Permission | Description | +| --- | --- | +| app_control.get | Read Application Control policies and rules | +| app_control.set | Create, modify and delete Application Control policies and rules, and their metadata | + ### External Adapters | Permission | Description | diff --git a/mkdocs.yml b/mkdocs.yml index 30e85c494..d12da5d46 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -417,6 +417,7 @@ nav: - Using Extensions: 5-integrations/extensions/using-extensions.md - LimaCharlie: - Overview: 5-integrations/extensions/limacharlie/index.md + - Application Control: 5-integrations/extensions/limacharlie/app-control.md - Artifact: 5-integrations/extensions/limacharlie/artifact.md - BinLib: 5-integrations/extensions/limacharlie/binlib.md - Cases: 5-integrations/extensions/limacharlie/cases.md @@ -529,6 +530,8 @@ nav: - D&R Rules: 7-administration/config-hive/dr-rules.md - YARA: 7-administration/config-hive/yara.md - Cloud Sensors: 7-administration/config-hive/cloud-sensors.md + - Application Control Policies: 7-administration/config-hive/app-control-policy.md + - Application Control Rules: 7-administration/config-hive/app-control-rule.md - Apps: 7-administration/config-hive/apps.md - Reference: From fca35f945688b503db0aaabdbb117135cbfa62e1 Mon Sep 17 00:00:00 2001 From: Maxime Lamothe-Brassard Date: Sun, 4 Oct 2026 03:22:11 +0000 Subject: [PATCH 2/3] Note that metadata without --enabled stores an Application Control record disabled Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/7-administration/config-hive/app-control-policy.md | 2 +- docs/7-administration/config-hive/app-control-rule.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/7-administration/config-hive/app-control-policy.md b/docs/7-administration/config-hive/app-control-policy.md index 8c1a909b1..16ac9b625 100644 --- a/docs/7-administration/config-hive/app-control-policy.md +++ b/docs/7-administration/config-hive/app-control-policy.md @@ -77,7 +77,7 @@ See [Permissions](../../8-reference/permissions.md#application-control). ## Examples -All examples use the CLI generic hive commands. Pass `--oid ` if your CLI is not already pointed at the organization. Records created without metadata are enabled by default in this hive, and the examples pass `--enabled` anyway so the intent is visible. +All examples use the CLI generic hive commands. Pass `--oid ` if your CLI is not already pointed at the organization. Records created without metadata are enabled by default in this hive, and the examples pass `--enabled` anyway so the intent is visible. Setting any metadata on create, such as a comment, tags or an expiry, without also passing `--enabled` stores the record disabled. ### A Windows allowlist policy diff --git a/docs/7-administration/config-hive/app-control-rule.md b/docs/7-administration/config-hive/app-control-rule.md index 09e4931d0..5011d8fdc 100644 --- a/docs/7-administration/config-hive/app-control-rule.md +++ b/docs/7-administration/config-hive/app-control-rule.md @@ -29,7 +29,7 @@ Two things live in the record's metadata rather than in its data: - A comment explaining why the rule exists goes in the metadata `comment`. - A temporary exception takes a metadata `expiry`. See [A temporary exception](#a-temporary-exception). -Only enabled records apply. Records created without metadata are enabled by default in this hive. +Only enabled records apply. Records created without metadata are enabled by default in this hive. Setting any metadata on create, such as a comment, tags or an expiry, without also passing `--enabled` stores the record disabled. ## Rule kinds From f4a0c7f98f8369dfa3365ecd785da85377b14b37 Mon Sep 17 00:00:00 2001 From: Maxime Lamothe-Brassard Date: Sun, 4 Oct 2026 13:27:04 +0000 Subject: [PATCH 3/3] App Control docs: numeric event fields, safe rollback wording APP_CONTROL_IS_ENFORCED, APP_CONTROL_MODE and APP_CONTROL_DECISION are numbers on the wire (confirmed on live events), so the example D&R rule matching false would never fire; it now matches 0. Removing a tag only steps a sensor back when a broader policy still matches it; a sensor that matches nothing keeps its last policy. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../extensions/limacharlie/app-control.md | 14 +++++++------- .../config-hive/app-control-policy.md | 4 ++-- docs/8-reference/edr-events.md | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/5-integrations/extensions/limacharlie/app-control.md b/docs/5-integrations/extensions/limacharlie/app-control.md index 7fda77c9e..f85698cf8 100644 --- a/docs/5-integrations/extensions/limacharlie/app-control.md +++ b/docs/5-integrations/extensions/limacharlie/app-control.md @@ -56,13 +56,13 @@ A deny rule always wins. Start in a mode that cannot block and move forward only once the reports are quiet. Tags make this easy because a policy can target a tag, and moving a sensor between stages is a tag change. The [policy page](../../../7-administration/config-hive/app-control-policy.md#staged-rollout-by-tag) has the three policies for this flow. -1. **Observe in `permissive`.** Create an allowlist policy for the platform with no tag filter. Add the rules you already know you need (your software publishers, your standard install locations). Every execution that the policy would deny shows up as an `APP_CONTROL_DENIED` event with `APP_CONTROL_IS_ENFORCED` false. Nothing is blocked and process start is not slowed. +1. **Observe in `permissive`.** Create an allowlist policy for the platform with no tag filter. Add the rules you already know you need (your software publishers, your standard install locations). Every execution that the policy would deny shows up as an `APP_CONTROL_DENIED` event with `APP_CONTROL_IS_ENFORCED` set to `0`. Nothing is blocked and process start is not slowed. 2. **Fix the rules.** Read the would-be blocks (see [Reading would-be blocks](#reading-would-be-blocks)). For each legitimate program, add an allow rule. Prefer a `signer` rule for software that updates, and use a `path` rule only for locations ordinary users cannot write to. Repeat until the legitimate noise is gone. Use a [temporary exception](../../../7-administration/config-hive/app-control-rule.md#a-temporary-exception) for one-off cases. 3. **Soak a pilot in `permissive_sync`.** Add a policy that targets a pilot tag, such as `app-control-soak`, in `permissive_sync`. These sensors run the full blocking path but still allow everything. This is the last chance to find a problem before blocking. 4. **Enforce the pilot.** Add a policy with a lower priority number than the other two that targets `app-control-enforce` in `enforcing`. Tag a small group of machines and watch them. 5. **Widen.** Tag more machines. Keep a broad `permissive` policy at the end of the order so that untagged machines keep reporting. -To step back at any point, remove the tag, or set the policy to `permissive` or `off`. The change reaches sensors on their next sync. +To step back at any point, set the policy to `permissive` or `off`, or remove the tag so that the sensor falls through to the broad `permissive` policy. A sensor that no longer matches any policy keeps the last policy it received, so keep that broad policy in place. The change reaches sensors on their next sync. !!! warning "Deleting does not disarm" Removing a policy, or unsubscribing from the extension, does not disarm sensors that already hold a policy. They keep enforcing it. To stand enforcement down, set the policy to `mode: off` and let sensors sync before you remove anything. @@ -74,7 +74,7 @@ To step back at any point, remove the tag, or set the policy to `permissive` or Application Control reports through two events, available on Windows and macOS. See the [EDR events reference](../../../8-reference/edr-events.md#app_control_denied) for the full fields. -`APP_CONTROL_DENIED` means the policy denied an execution. If `APP_CONTROL_IS_ENFORCED` is true, the sensor blocked it. If it is false, the sensor is in `permissive` or `permissive_sync` and only reports what it would have blocked. +`APP_CONTROL_DENIED` means the policy denied an execution. If `APP_CONTROL_IS_ENFORCED` is `1`, the sensor blocked it. If it is `0`, the sensor is in `permissive` or `permissive_sync` and only reports what it would have blocked. `APP_CONTROL_UNRESOLVED` means the sensor could not evaluate an execution and allowed it. @@ -88,7 +88,7 @@ Useful fields on `APP_CONTROL_DENIED`: | `APP_CONTROL_SIGNING_ID` | The macOS code-signing identifier the sensor saw. | | `APP_CONTROL_REASON` | Why the sensor reached the decision. | | `APP_CONTROL_MATCHED_RULE` | Optional. The rule that matched, when there is one. | -| `APP_CONTROL_MODE` | The mode of the policy in effect. | +| `APP_CONTROL_MODE` | The mode of the policy in effect, as a number: `0` off, `1` permissive, `2` permissive_sync, `3` enforcing. | | `APP_CONTROL_GENERATION` | The generation of the policy the sensor was running. | To turn would-be blocks into something you can list and count, write a D&R rule that reports them: @@ -98,15 +98,15 @@ detect: event: APP_CONTROL_DENIED op: is path: event/APP_CONTROL_IS_ENFORCED - value: false + value: 0 respond: - action: report name: app-control-would-block ``` -Group the resulting detections by `FILE_PATH` or signer to see which programs matter most. A signer that appears on many machines is a candidate for a `signer` allow rule. A path seen on one machine is usually a one-off. To alert on actual blocks instead, match `true` and change the report name. +Group the resulting detections by `FILE_PATH` or signer to see which programs matter most. A signer that appears on many machines is a candidate for a `signer` allow rule. A path seen on one machine is usually a one-off. To alert on actual blocks instead, match `1` and change the report name. -Watch `APP_CONTROL_UNRESOLVED` during the soak steps. Each one is an execution the sensor let through because it could not decide, so it is a gap in what the policy covers. +Watch `APP_CONTROL_UNRESOLVED` during the `permissive_sync` soak. Each one is an execution the sensor let through because it could not check it in time, for example when the file hash was not available. ## Managing from the CLI diff --git a/docs/7-administration/config-hive/app-control-policy.md b/docs/7-administration/config-hive/app-control-policy.md index 16ac9b625..5092a00a3 100644 --- a/docs/7-administration/config-hive/app-control-policy.md +++ b/docs/7-administration/config-hive/app-control-policy.md @@ -29,7 +29,7 @@ Both hives are partitioned by organization, like the other Config Hive types. Ea Keywords are case-sensitive and must be lowercase exactly as shown. Unknown fields are refused when you save the record. !!! warning "A locked-out allowlist is refused" - An `enforcing` policy with stance `allowlist` and `trust_os_vendor: false` is refused on save. A sensor cannot apply it, because it would block the operating system itself. + An `enforcing` policy with stance `allowlist` and `trust_os_vendor: false` is refused on save. Sensors refuse to enforce an allowlist that does not trust the operating system vendor, so the policy would never take effect. ## Which policy a sensor gets @@ -50,7 +50,7 @@ Because the first match wins, you stage a rollout by putting narrow policies (a | `permissive_sync` | The sensor runs the same blocking path as `enforcing`, then always allows. Nothing is blocked. Use this as the last soak step before enforcing. | | `enforcing` | The sensor blocks executions that the policy denies. | -In `permissive` and `permissive_sync`, a would-be block is reported as an `APP_CONTROL_DENIED` event with `APP_CONTROL_IS_ENFORCED` set to false. See [Reading would-be blocks](../../5-integrations/extensions/limacharlie/app-control.md#reading-would-be-blocks). +In `permissive` and `permissive_sync`, a would-be block is reported as an `APP_CONTROL_DENIED` event with `APP_CONTROL_IS_ENFORCED` set to `0`. See [Reading would-be blocks](../../5-integrations/extensions/limacharlie/app-control.md#reading-would-be-blocks). ## Stance diff --git a/docs/8-reference/edr-events.md b/docs/8-reference/edr-events.md index 9f7fdcc03..59a59dd53 100644 --- a/docs/8-reference/edr-events.md +++ b/docs/8-reference/edr-events.md @@ -119,18 +119,18 @@ These are the events emitted by the endpoint agent for each supported operating ### APP\_CONTROL\_DENIED -Generated by [Application Control](../5-integrations/extensions/limacharlie/app-control.md) when the policy denies an execution. `APP_CONTROL_IS_ENFORCED` tells you whether the sensor blocked it (`true`) or, in a `permissive` or `permissive_sync` policy, only reported a would-be block (`false`). +Generated by [Application Control](../5-integrations/extensions/limacharlie/app-control.md) when the policy denies an execution. `APP_CONTROL_IS_ENFORCED` tells you whether the sensor blocked it (`1`) or, in a `permissive` or `permissive_sync` policy, only reported a would-be block (`0`). | Field | Description | | --- | --- | | `FILE_PATH` | Path of the program. | | `PROCESS_ID` | Process ID of the execution. | | `HASH` | SHA-256 of the file. Optional. | -| `APP_CONTROL_DECISION` | The decision the sensor reached. | +| `APP_CONTROL_DECISION` | The decision the sensor reached: `1` deny, `2` unresolved. | | `APP_CONTROL_REASON` | The reason for the decision. | -| `APP_CONTROL_MODE` | Mode of the policy in effect. | +| `APP_CONTROL_MODE` | Mode of the policy in effect: `0` off, `1` permissive, `2` permissive_sync, `3` enforcing. | | `APP_CONTROL_GENERATION` | Generation of the policy the sensor was running. | -| `APP_CONTROL_IS_ENFORCED` | Whether the denial was enforced. | +| `APP_CONTROL_IS_ENFORCED` | `1` if the sensor blocked the execution, `0` if it only reported it. | | `APP_CONTROL_MATCHED_RULE` | The rule that matched. Optional. | | `APP_CONTROL_SIGNER` | The signer the sensor saw. | | `APP_CONTROL_SIGNING_ID` | The code-signing identifier the sensor saw (macOS). |