---
title: "Common Expression Language (CEL) - Santa Docs"
description: "Common Expression Language (CEL) - Open source binary authorization for macOS. Configure, deploy, and extend the agent that powers Workshop."
doc_version: "1"
last_updated: "2026-10-07"
canonical: "https://northpole.security/docs/santa/cookbook/cel"
---
# Common Expression Language (CEL)

This page lists well-known and/or community-contributed CEL expressions.

CEL ([Common Expression Language](https://cel.dev/)) rules allow for more complex policies than would normally be possible. Read how to configure CEL rules in the [Binary Authorization](https://northpole.security/docs/santa/features/binary-authorization#cel) documentation.

## Apps signed since X

This will prevent executions of an app where the specific binary was signed before the provided date. This is particularly useful when attached to a `TEAMID` or `SIGNINGID` rule.

```
target.signing_time >= timestamp('2025-05-31T00:00:00Z')
```

## Apps signed within the last N days 2026.6

This allows executions only when the binary was securely signed within a sliding window — here, the last 90 days — and blocks anything older. Unlike a fixed `timestamp(...)`, the window moves forward automatically each day, so the rule never needs to be re-pushed. `today()` is the start of the current day in the host’s time zone (before Santa 2026.8, the current UTC day) and `days(n)` is `n`×24h (the standard `duration()` only parses units up to hours). This requires [Workshop](https://northpole.security/), and because `today()` changes daily the result is not cached.

```
target.secure_signing_time > today() - days(90)
```

The example binary below was signed in 2020, so it falls outside the window and is blocked:

## Allow an app only during working hours 2026.8

Attach this to a Signing ID or Team ID rule for the application. It allows the app from 09

to 17

, Monday through Friday, on each host’s own clock, and blocks it at any other time. See [Time Based Rules](https://northpole.security/docs/santa/features/time-based-rules) for the other window forms and for what happens at the edges of a window. Requires [Workshop](https://northpole.security/) 2026.8 or later and Santa 2026.8 or later, and because the window is checked at every execution the result is not cached.

```
policy_for_range(weekdays(), "09:00", "17:00", ALLOWLIST, BLOCKLIST)
```

The example below evaluates the rule at 10

on a Monday, so the execution is allowed. Change `now` to an evening or a weekend to see the block:

## Working hours with Touch ID outside them 2026.8

Out-of-hours use stays possible with a person at the keyboard. The cooldown means one approval covers the next hour. Requires [Workshop](https://northpole.security/) 2026.8 or later and Santa 2026.8 or later.

```
policy_for_range(weekdays(), "08:00", "18:00", ALLOWLIST, require_touchid_with_cooldown_minutes(60))
```

The example below evaluates the rule at 21

on a Monday, so Touch ID is required:

## Require a hardware security key on a Mac without Touch ID 2026.9

Touch ID is unavailable on a desktop Mac and in clamshell mode, which leaves a password as the only way to approve. A security key covers those machines with something that has to be physically present and cannot be typed. Use `REQUIRE_PRESENCE` where either will do. Requires Santa 2026.9 or later.

Like Touch ID, this establishes that a person was there, not which person. See [User Authorization](https://northpole.security/docs/santa/features/binary-authorization#user-authorization).

```
euid == 0 ? REQUIRE_SECURITY_KEY : ALLOWLIST
```

The example below runs the tool as root, so a key touch is required:

## Audit out-of-hours use before enforcing 2026.8

Both policy slots allow the process to run. Out-of-hours executions arrive as audit matches, which is the list of users a blocking version of this rule would have stopped. Swap `AUDIT` for `BLOCKLIST` when that list looks right. Requires [Workshop](https://northpole.security/) 2026.8 or later and Santa 2026.8 or later.

```
policy_for_range(weekdays(), "09:00", "17:00", ALLOWLIST, AUDIT)
```

The example below evaluates the rule on a Saturday afternoon, so the execution is allowed and flagged for audit:

## One maintenance window for the whole fleet 2026.8

With a named time zone every host reads the same calendar, so this window is the same four hours everywhere regardless of each host’s own zone. The zone can be `"UTC"`, an IANA name such as `"America/New_York"`, or a fixed offset such as `"+05:30"`. Requires [Workshop](https://northpole.security/) 2026.8 or later and Santa 2026.8 or later.

```
policy_for_range([0, 1, 2, 3, 4, 5, 6], "01:00", "05:00", "UTC", ALLOWLIST, BLOCKLIST)
```

The example below runs on a host in New York at 23

local time, which is 03

UTC, so the execution is allowed:

## Quit an app when the shift ends 2026.8

Without `kill_on_expiry()` a window governs new executions only, and a process started inside the window keeps running after it closes. Wrapping the in-range policy records every execution the rule allows during the window and quits whatever is still running at 17

, after warning the user 48 minutes earlier. See [Quitting Processes When the Window Closes](https://northpole.security/docs/santa/features/time-based-rules#kill-on-expiry) for what Santa records and how the quit is delivered. Requires [Workshop](https://northpole.security/) 2026.8 or later and Santa 2026.8 or later.

```
policy_for_range(weekdays(), "09:00", "17:00", kill_on_expiry(ALLOWLIST), BLOCKLIST)
```

The example below evaluates the rule at 15

on a Monday. The execution is allowed, and the playground shows the quit that is recorded for 17:00:

## Timed access counted from launch 2026.8

The duration form opens a window at the moment of the execution, so it is always in range and exists only to set an expiry, which is why `kill_on_expiry()` is required with it. Here the user is asked for Touch ID first, and the process is quit 30 minutes after it started. A second launch inside those 30 minutes shares the first one’s deadline. Requires [Workshop](https://northpole.security/) 2026.8 or later and Santa 2026.8 or later.

```
policy_for_range(duration("30m"), kill_on_expiry(require_touchid_with_cooldown_minutes(30)))
```

To time only some launches, put the call in a branch of a ternary. Ordinary launches are allowed at any hour and only launches with `--beta` are timed:

```
"--beta" in args ? policy_for_range(duration("30m"), kill_on_expiry(ALLOWLIST)) : ALLOWLIST
```

## Prevent users from disabling gatekeeper

Create a signing ID rule for `platform:com.apple.spctl` and attach the following CEL program

```
[
    '--global-disable',
    '--master-disable',
    '--disable',
    '--add',
    '--remove'
].exists(flag, flag in args) ? BLOCKLIST : ALLOWLIST
```

## Prevent Timestomping of LaunchAgents and LaunchDaemons Santa 2025.8

Malware like those produced by the Chollima groups use “timestomping” to reset the timestamps of LaunchAgents and LaunchDaemons using touch. This can be prevented / detected by creating a SigningID rule for `platform:com.apple.touch` with the following CEL program.

This technique was recently discussed by [Jaron Bradely](https://themittenmac.com/author/jaron-bradley/) at [Objective by the Sea v8](https://objectivebythesea.org/v8/talks.html#Speaker_24)

```
args.exists(arg, arg in [
  '-a', '-m', '-r', '-A', '-t'
]) && args.join(" ").contains("Library/Launch") ? BLOCKLIST : ALLOWLIST
```

Note this will not stop using the system calls directly or otherwise programmatically modifying the timestamps. Also this won’t cover modifications if the process’ current working directory is already in the LaunchDaemons / LaunchAgents directories.

## Prevent OSAScript From Popping Password Dialogs Santa 2025.8

A lot of malware on macOS will attempt to get users to enter their passwords into a dialog box via osascript. This is a basic rule to stop directly asking for a password dialog.

Make a SigningID rule for `platform:com.apple.osascript` with the following CEL Program

```
(
    args.join(" ").lowerAscii().matches(".*\\W+with\\W+hidden\\W+answer.*") ||
    args.join(" ").lowerAscii().contains("password")
) &&
    args.join(" ").lowerAscii().matches(
        ".*\\W+display\\W+dialog.*")  ? BLOCKLIST : ALLOWLIST
```

Note: This will not stop obfuscated osascript that’s evaluated at runtime or any other malicious behavior triggered through osascript. For better security block osascript all together if you can. Be aware software like the Google Cloud SDK installer and AI tools like claude code use osascript.

Also if you’re using osascript to do this legitimately this will break your usage.

## Prevent users from enabling SSH and Remote Apple Events Santa 2025.8

As called out in [loobins](https://www.loobins.io/binaries/systemsetup/) the systemsetup command can be used to enable SSH and Remote Apple Events via command line options.

To block this create a signing ID rule for `platform:com.apple.systemsetup` and attach the following CEL program:

```
args.join(" ").contains("-setremotelogin on") ||
args.join(" ").contains("-setremoteappleevents on") ? BLOCKLIST : ALLOWLIST
```

## Prevent Users from Taking and Mounting Time Machine Snapshots

As was presented at [Kawaiicon 2025](https://kawaiicon.org/) by [Calum Hall](https://www.youtube.com/watch?v=hIeNuqq12sk&t=1390s), Time Machine snapshots can be used to bypass [File Access Authorization rules](https://www.youtube.com/watch?v=hIeNuqq12sk&t=1390s).

You can stop the taking of local snapshots by creating a signing ID for `platform:com.apple.tmutil` and attaching the following CEL program:

```
'localsnapshot' in args ? BLOCKLIST : ALLOWLIST
```

This will break taking local snapshots via the command line. Alternatively if you need to still be able to take time machine snapshots but don’t want users to mount them locally you can stop the mount of local snapshots with a signing ID rule `platform:com.apple.mount_apfs` with the following CEL program

```
('-s' in args &&
  args.exists(arg, arg.contains("com.apple.TimeMachine."))) ? BLOCKLIST : ALLOWLIST
```

## Sitemap

- [Home](https://northpole.security/index.md)
- [Workshop](https://northpole.security/workshop.md)
- [Santa](https://northpole.security/santa.md)
- [Features](https://northpole.security/features.md)
- [Cookbook](https://northpole.security/cookbook.md)
- [Docs](https://northpole.security/docs.md)
- [Blog](https://northpole.security/blog.md)
- [Glossary](https://northpole.security/glossary.md)
- [About](https://northpole.security/about.md)
- [Contact](https://northpole.security/contact.md)
