# North Pole Security: full documentation > Concatenation of all Workshop documentation, Santa documentation, and blog posts on northpole.security. Follows the llmstxt.org full-text convention. # Workshop documentation ## Introduction # Introduction to Workshop Welcome to Workshop, the comprehensive administration console for managing Santa deployments across thousands of hosts. Workshop provides a centralized interface for monitoring, configuring, and maintaining your Santa endpoint security infrastructure. For more detailed information about Santa, visit the [Santa Documentation](https://northpole.dev/). --- ## AI # AI Workshop provides AI-powered features to help you manage and understand your endpoint security environment. ## AI Chat AI Chat lets you ask natural-language questions about your Workshop data directly from the dashboard. Chat sessions have the same permissions as the logged-in user — the AI assistant can only access data you're authorized to see. ### Setup 1. Go to Settings → AI → Chat 2. Toggle **Enabled** 3. Select an AI provider (Anthropic, OpenAI, or Google) 4. Enter your API key for the chosen provider 5. Optionally select a specific model (defaults are recommended) ### Privacy :::warning Information you send to AI Chat — including your questions, conversation history, and any Workshop data retrieved by the assistant — is sent to whichever third-party AI provider your organization has configured. Review your provider's data handling policies before enabling AI Chat. ::: ### Supported Providers - Anthropic - OpenAI - Google ### What You Can Do AI Chat can query your Workshop data using the same API methods available through the web interface. Example questions: > Show me a summary of all rules in Workshop. > Why is <app name> blocked on <host name>? > What are the top 10 most executed applications across my fleet? > Are any of my hosts out of date? The assistant uses tools to look up data, perform calculations, and query Workshop documentation. By default, the assistant cannot modify your configuration. To enable write access, toggle **Read-Write Mode** in AI Chat settings. --- ## MCP Server The Model Context Protocol (MCP) is an open protocol that standardizes how applications provide context to large language models (LLMs). Learn more at [modelcontextprotocol.io](https://modelcontextprotocol.io). Workshop's MCP server exposes all of the methods available in the Workshop API to MCP-compatible clients such as Claude Desktop, Claude Code, LM Studio, and Gemini CLI. ### Getting Started #### 1. Enable the MCP Server 1. Go to Settings → AI → MCP 2. Toggle the switch to enable the MCP server :::warning By default the MCP server only allows read-only access, even if you have added `write` permissions to the API key or OAuth scope. You must enable read-write mode in the MCP settings to allow MCP clients to make changes. ::: #### 2. Choose an Authentication Method {#choose-auth-method} **OAuth 2.0 (recommended):** MCP clients that support OAuth will automatically prompt you to log in — no extra setup needed. Just point the client at your Workshop MCP URL and authenticate through the browser. **API key (alternative):** If your MCP client doesn't support OAuth, or you prefer key-based auth, generate an API key: 1. Go to Settings → API Keys 2. Click "Create API Key" 3. Copy the key (it starts with `npsws_sk_`) ### Authentication #### OAuth 2.0 MCP clients that support OAuth 2.0 can authenticate using your organization's identity provider. This is the recommended approach. OAuth users receive permissions based on their Workshop role assignment. The MCP read-write toggle in settings provides an additional layer of control over write access. #### API Key Alternatively, create an API key with the desired permissions and pass it in the `Authorization` header. See [Choose an Authentication Method](#choose-auth-method) above. ### Integrating with MCP #### Claude Desktop 1. **Install Claude Desktop** from [claude.ai](https://claude.ai/download) 2. Open **Settings** → **Connectors** 3. Click **Add custom connector** 4. Enter your Workshop MCP URL: `https://example.workshop.cloud/mcp` 5. Click **Add** — Claude will open a browser window for OAuth authentication See the [Claude custom connectors documentation](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp) for more details. #### Claude Code 1. **Install Claude Code** from [claude.ai](https://claude.ai/download) 2. Run the following command to add the Workshop MCP server: ``` claude mcp add --transport http workshop https://example.workshop.cloud/mcp ``` Claude Code will open a browser window for OAuth authentication when you first connect. See the [Claude Code MCP documentation](https://docs.anthropic.com/en/docs/claude-code/mcp) for more details. #### LM Studio 1. **Install LM Studio** from [lmstudio.ai](https://lmstudio.ai) 2. Open the **Program** tab in the right sidebar 3. Click **Install** → **Edit mcp.json** and add: ```json { "mcpServers": { "workshop": { "url": "https://example.workshop.cloud/mcp", "headers": { "Authorization": "npsws_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } } } } ``` As of March 2026, LM Studio does not support OAuth for remote MCP servers, so an API key is required. See the [LM Studio MCP documentation](https://lmstudio.ai/docs/app/mcp) for more details. #### Gemini CLI 1. **Install Gemini CLI** from [github.com/google-gemini/gemini-cli](https://github.com/google-gemini/gemini-cli) 2. Run the following command to add the Workshop MCP server: ``` gemini mcp add --transport http workshop https://example.workshop.cloud/mcp ``` See the [Gemini CLI MCP documentation](https://geminicli.com/docs/tools/mcp-server/) for more details. ### Example Prompts > Show me a summary of all rules in Workshop and use terms from the documentation to explain them. > Why is <app name> blocked on <host name> in Workshop? > Are any of my Workshop hosts out of date? > Are my Workshop hosts ready to switch from Monitor Mode to Lockdown Mode? --- ## API Reference # API Reference ## Quick Start The Workshop API uses [Connect RPC](https://connectrpc.com/) for [protobuf](https://protobuf.dev/)-based RPC. To make an API call, first generate an API key in the [API Keys](./api-keys) page. ### Using gRPC You can use [grpcurl](https://github.com/fullstorydev/grpcurl) to experiment with the API: ```sh $ grpcurl \ -d '{"uuid": "00000000-0000-0000-0000-000000000000"}' \ -H 'Authorization: npsws_sk_xxxxxxxxxxxxxx' \ example.workshop.cloud:443 \ workshop.v1.WorkshopService/GetHost ``` :::tip If you see an HTTP 464 error or "malformed header: missing HTTP content-type" when making gRPC requests, try adding `api.` to the beginning of your domain e.g. api.example.workshop.cloud ::: ### Using HTTP You can use HTTP clients like `curl` to make API calls. Refer to the [Connect documentation](https://connectrpc.com/docs/curl-and-other-clients) for more details. Mutable methods must be called with a `POST` request: ```sh $ curl \ -X POST \ -H 'Authorization: npsws_sk_xxxxxxxxxxxxxx' \ --json '{"uuid": "00000000-0000-0000-0000-000000000000"}' \ https://example.workshop.cloud/workshop.v1.WorkshopService/UpdateHost ``` Immutable methods can be called with a `GET` request: ```sh $ curl \ --get \ --data-urlencode 'encoding=json' \ --data-urlencode 'message={}' \ -H 'Authorization: npsws_sk_xxxxxxxxxxxxxx' \ https://example.workshop.cloud/workshop.v1.WorkshopService/ListAuditEvents ``` :::warning API methods that support `GET` will have the following option: ```proto option idempotency_level = NO_SIDE_EFFECTS; ``` ::: ## API Methods For available API methods, see the [workshop.proto reference](https://buf.build/northpolesec/workshop-api/docs/main:workshop.v1#workshop.v1.WorkshopService). --- ## API Keys # API Keys The API Keys interface provides a way to create and manage API keys for programmatic access to Workshop. These keys allow automated systems and scripts to interact with the Workshop API without requiring user authentication. Workshop API keys follow a specific format to ensure security and traceability: - Prefix: `npsws_sk_` - Identifies the key as a Workshop secret key - Body: A hexadecimal string that serves as the unique identifier and authentication token API Keys are immutable and cannot be changed after creation. If you need to change a key, you must create a new one. ## Overview The API Key dashboard displays information about each key: - **Name**: The unique name identifier for the API key - **Role**: The permission level assigned to the key (e.g., superadmin, readonly) - **Creator**: The user who created the API key - **Expires**: The expiration date and time of the key ## API Key Roles Workshop supports different roles for API keys: - **superadmin**: Full access to all API endpoints - **readonly**: Read-only access to API endpoints Each role determines which API endpoints the key can access based on the permission requirements defined in the API. ## Creating API Keys To create a new API key, click the "Create" button and fill in the required information: - **Key Name**: A descriptive name for the API key (5-50 characters) - **Role**: The permission level for the key - **Expiry**: How long the key will remain valid (1 week, 1 month, 3 months, or 1 year) ## Managing API Keys The API Keys dashboard allows you to: - **View existing keys**: See all API keys, their roles, creators, and expiration dates - **Delete keys**: Remove API keys that are no longer needed or may have been compromised --- ## Approval Workflows # Approval Workflows A powerful feature of Workshop is the ability to delegate approvals decisions across your organization when running in Lockdown mode. When a user successfully completes an approval workflow they will receive allow rules for that specific application on their machine(s). This allows users to stay in Lockdown mode while still only running approved software. ## Conditions for an Approval Workflow to Begin - Santa has blocked an application from running on a user's system - The user is running in [Lockdown mode](https://northpole.dev/features/binary-authorization/#client-mode) and does not have an explicit rule allowing it - The user is part of a tag that has approval workflows configured - The binary or bundle have not been flagged as malware or confirmed as malware - If the Risk Engine is enabled then all configured plugins must return an _allow_ decision ### Risk Engine When the Risk Engine is configured and enabled, it will act as a gating function before it will let a user start an approval workflow. This allows admin users to set a threshhold for how risky something is and decide if admins want to let users approve for themselves or should seek expert assistance. ## Flagging an Application During an approval workflows each user and approver is presented with the option to flag an application as malicious via a button. This allows users to report malware that could otherwise be missed by the Risk Engine plugins or applications that are potentially troublesome. If they do flag an application as potentially malicious then all approvals workflows for that application are halted. Only after an admin has restored the state of the binary via the blockables page or APIs will approvals continue. ## Auditing All approval workflows create audit events showing the rules that were created and the target for those rules, and which approver approved, and at which time approval was granted. ### Audit Events All Audit Events can be seen here. - _BLOCKABLE_FLAGGED_MALICIOUS_ If a user or an approver flags a Binary or Application Bundle (aka Blockable) as malicious this event is created. - _VOTE_CAST_ When a user in a self-service or a social voting has voted to approve their software. This contains the voting user. - _SELF_SERVICE_RULE_CREATION_ This audit event is created when a user has approved their own software and a rule was created for their machines. - _SOCIAL_VOTING_RULE_CREATION_ This audit event is created when a user is in a social voting approval workflow and one of the threshholds has been met or exceeded. - _DESIGNATED_APPROVER_REQUEST_ This is created when a user is in a designated approver workflow and has asked for an application. - _DESIGNATED_APPROVER_REQUEST_APPROVED_ This is created when an approver approves a designated approver request for an application. - _DESIGNATED_APPROVER_REQUEST_REJECTED_ This is created when an approver rejects a designated approver request for an application. - _DESIGNATED_APPROVER_RULE_CREATION_ This is created when an approver approves a designated approver request for an application and rules are created. ## Supported Approval Workflows ### Self-Service Approval The simplest approval workflow is self-service approval. This allows any user in a tag to approve their own software provided it meets all of the conditions for starting an approval workflow. ### Designated Approver Workflows The designated approver workflows allow specific users to act as the approvers for users in a given tag. Approvers need not be admins themselves. All designated approver workflows (Manager Approval, Approval by Specified Users, Approval By Members of a Tag) optionally support requiring justification from the requestor while requesting approval for software. Requiring justification can be enabled in approval workflow settings. Justification is limited to 1000 characters. #### Manager Approval When configured to use manager approval. A user's manager must approve before rules are created for that user. If they approve. A user's manager is determined based on their directory sync attributes or via the API. A user's manager can also be seen in the Settings page in the users section at the bottom. #### Approval By Specified Users The second designated approver workflow is the specific users workflow. In this workflow only users on the approvers list may approve software. Users will need to interact with and ask only these approvers. Additionally admins may configure threshholds where multiple approvers can be required before approval is granted. #### Approval By Members of a Tag The third designated approver workflow is the specific users workflow. In this workflow only users who are members of a group mapped to a specific tag may approve software. This is useful if admins have rotations or otherwise use group membership to rotate who can approve software. Admins may configure threshholds where multiple approvers can be required before approval is granted. ## Social Voting In social voting, any user in the specified tags can vote to approve an application. Each vote counts towards the local and global threshholds. Once an application has met the local threshhold (must be 2 or greater) the users in the tag that have voted to approve will receive an allow rule. After the local threshhold has been met additional votes will add the allow rule to those users who have voted. Upon reaching the global threshhold an allow rule is pushed out for the tag. Additionally multiple tags can be joined to share votes. This allows different user populations to share approvals for common software will still allowing for other restricions to remain. Setting this approval workflow on the global tag provides a workflow similar to Google's Upvote the original Santa sync service. ## Configuring Approval Workflows All approval workflows can be configured via the UI as part of the tag settings. Additionally, approval workflows can be specified via the [UpdateApprovalWorklowSettings](https://buf.build/northpolesec/workshop-api/docs/main:workshop.v1#workshop.v1.WorkshopService.UpdateApprovalWorkflowSettings) API --- ## Audit # Audit Every change made to Workshop, whether by UI or API, is recorded in the audit logging system. This provides a complete record of all actions taken in your Workshop deployment for security, compliance, and debugging purposes. ## Event Types Audit events are categorized by the type of resource or action being performed. Each event includes: - **ID**: A unique identifier for the event - **Transaction ID**: Links related events together (e.g., a vote that triggers rule creation) - **Timestamp**: When the event occurred - **Actor**: Who initiated the action (user, API key, host, or system) - **Event Type**: The specific action performed - **Resource**: The identifier of the affected resource - **Outcome**: Whether the action succeeded, failed, or was rejected - **Details**: Additional context about the event (often includes JSON data) - **Previous Value**: For update operations, the state before the change ### Example Event Types Here are some common audit events tracked by Workshop: **API Keys** - `APIKEY_CREATE`: A new API key was created - `APIKEY_DELETE`: An API key was deleted **Rules** - `RULE_UPSERT`: A rule was created or updated - `RULE_DELETE`: A rule was removed **Hosts** - `HOST_CREATE`: A new host registered with Workshop - `HOST_UPDATE`: Host information was modified - `HOST_SYNC`: Host synchronized with Workshop - `HOST_CLEAN_SYNC`: Host performed a clean sync (full rule refresh) - `HOST_MANUAL_PUSH`: Rules were manually pushed to a host **Tags** - `TAG_CREATE`: A new tag was created - `TAG_DELETE`: A tag was removed - `TAG_SET_ORDER`: Tag resolution order was changed **Settings** - `SETTINGS_UPDATE_SYNC_SETTINGS`: Santa sync settings were updated - `SETTINGS_TELEMETRY_CLOUD_BUCKET_UPDATE`: Telemetry export bucket configured - `APPROVAL_WORKFLOW_SETTINGS_UPDATE`: Approval workflow settings changed **Approval Workflows** - `SELF_SERVICE_RULE_CREATION`: User created a rule via self-service - `DESIGNATED_APPROVER_REQUEST`: Approval request was submitted - `DESIGNATED_APPROVER_REQUEST_APPROVE`: Request was approved - `DESIGNATED_APPROVER_REQUEST_REJECT`: Request was rejected - `VOTE_CAST`: A vote was cast on a blockable **Risk Engine** - `RISK_ENGINE_EXCEPTION_CREATE`: A risk engine exception was created - `BLOCKABLE_FLAG_MALICIOUS`: A blockable was flagged as malicious ## Viewing Audit Events ### Accessing the Audit Log Navigate to the Audit page in the Workshop UI to view all audit events. The audit table provides: - **Filtering**: Search and filter by event type, actor, resource, outcome, and date range - **Sorting**: Sort by timestamp, event type, or outcome - **Expandable Rows**: Click any row to see full event details including JSON diffs for updates - **Transaction Linking**: Click a transaction ID to view all related events ### Querying Examples **Filter by event type:** Use the event type filter to show only specific types of events, such as all rule changes or host syncs. **Filter by actor:** Find all actions performed by a specific user, API key, or host by filtering on the actor field. **Filter by date range:** Select a date range to view events within a specific time period. **View related events:** Click on a transaction ID to see all events that are part of the same transaction. This is useful for tracking complex operations like approval workflows that generate multiple audit events. ### Event Details When you expand an audit event row, you'll see: - Complete event metadata (ID, transaction ID, timestamp, actor) - The full resource identifier - Detailed information about what changed - For update operations, a side-by-side diff showing before and after values ## Audit Log Export Workshop can automatically export audit logs to cloud storage for long-term retention, compliance requirements, or integration with external SIEM systems. See the [Event Export documentation](./event-export) for detailed information on configuring audit event export, including cloud storage setup and export behavior. --- ## Event Export # Event Export Workshop can automatically export events to cloud storage for long-term retention, compliance requirements, or integration with external SIEM systems and analytics platforms. ## Supported Event Types Workshop supports exporting three types of events: - **Audit Events**: All changes made to Workshop (rules, settings, tags, etc.) - **Execution Events**: Santa execution events from macOS endpoints - **File Access Events**: Santa file access monitoring events from macOS endpoints - **Removable Media Events**: Santa removable media (e.g. USB mass storage device) mount events from macOS endpoints - **Network Mount Events**: Santa network mount events from macOS endpoints Each event type can be configured independently with its own cloud storage bucket. ## Configuring Event Export Navigate to Settings → Event Export to configure export settings for each event type. ### Audit Event Export Audit events track all changes made to Workshop, whether by UI or API. Exporting audit logs provides a complete record of all actions for security, compliance, and debugging purposes. **To configure:** 1. Navigate to the Audit Events section 2. Enter your cloud storage bucket URL in one of these formats: - AWS S3: `s3://your-bucket-name` - Google Cloud Storage: `gs://your-bucket-name` 3. Click **Save Changes** See the Audit documentation for more details on audit event types and viewing audit logs. ### Execution Event Export Execution events record all binary executions detected by Santa on your macOS endpoints. This includes allowed and blocked executions, along with binary metadata and host information. **To configure:** 1. Navigate to the Execution Events section 2. Enter your cloud storage bucket URL 3. Click **Save Changes** Execution events include details such as: - Binary SHA-256, file path, and signing information - Execution decision (allowed, blocked, or blocked by bundle) - Host information (hostname, primary user, OS version) - Process information (PID, PPID, executing user) - Tags applied to the host at execution time ### File Access Event Export File access events record Santa's file access monitoring activity, which tracks access to protected paths on your endpoints. **To configure:** 1. Navigate to the File Access Events section 2. Enter your cloud storage bucket URL 3. Click **Save Changes** File access events include: - Accessed file path and details - Access type and decision - Process information for the accessing application - Host and user information ### Removable Media Event Export Removable media event record Santa's mount monitoring and blocking activity for things like USB devices. **To configure:** 1. Navigate to the Removable Media Events section 2. Enter your cloud storage bucket URL 3. Click **Save Changes** Removable media events include: - Device details like protocol, model and vendor - Mount path and decision - Remount arguments if configured - Host and user information ### Network Mount Event Export Network mount event record Santa's network filesystem mount monitoring and blocking activity. **To configure:** 1. Navigate to the Network Mount Events section 2. Enter your cloud storage bucket URL 3. Click **Save Changes** Network mount events include: - Mount path from and mount path on - Filesystem type - Host and user information ## Cloud Storage Access The Workshop service account must have read/write access to the specified buckets. Both AWS S3 and Google Cloud Storage are supported. You can provide access to your bucket to the Workshop service role using a bucket policy like the one below, replacing `<123456789123>` and `` appropriately: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::<123456789123>:role/WorkshopTaskExecutionRole" }, "Action": ["s3:PutObject"], "Resource": ["arn:aws:s3:::/*"] } ] } ``` In the Google Cloud console, you can give the necessary access to the Workshop service account principal with the "Storage Object Creator" role. You can also do this with the gcloud CLI, replacing `` and `` as appropriate: ```shell gcloud storage buckets add-iam-policy-binding gs:// \ --member=serviceAccount: \ --role=roles/storage.objectCreator ``` :::tip You can use the same bucket for all three event types, or configure separate buckets for organizational purposes. Events are written to different file paths based on type. ::: ## Export Behavior ### Scheduling - Events are exported periodically - Audit events are exported once per hour - Execution and File Access events are exported every 10 minutes - Each export batch is limited to 25,000 events - If more events are available after a batch, export continues automatically until fewer than 1,000 events remain - Exports run independently for each event type ### Initial Export - If you have a large number of existing events, the initial export after configuration may take some time to complete as it works through the backlog in batches - The export process will gradually work through historical events until it's caught up ### Progress Tracking - The export process tracks the last exported event ID for each event type - You can view the last exported event ID in the Settings page to monitor export progress - Click on the event ID to view that specific event in Workshop - Export resumes automatically from the last checkpoint if interrupted ### Data Format - Events are written as newline-delimited JSON (NDJSON) files - Each line in the exported files is a complete JSON object representing one event - Files are organized by event type and timestamp - All event fields are included in the export (IDs, timestamps, metadata, details, etc.) --- ## Events # Events The Events interface provides visibility into events across your Santa-protected fleet. This feature lets administrators track, analyze, and respond to binary executions throughout your organization. ## Overview The Events interface displays information about each execution event: - **Host Name**: The name of the endpoint where the event occurred - **File Name**: The name of the executed binary - **Decision**: Whether the execution was allowed or blocked - **Reason**: The reason for the decision - **Timestamp**: When the execution attempt occurred ## Event Details Clicking on a file name in the events table opens a detailed view with additional information about the binary: - **SHA-256**: The cryptographic hash of the binary - **CDHash**: The code directory hash used by macOS for code signing verification - **Team ID**: The Apple Developer Team ID associated with the binary - **Signing ID**: The signing identity used to sign the binary - **Entitlements**: A list of entitlements granted to the binary - **First Seen**: When this binary was first observed in your environment ### Creating Rules from Event Details From the event details page, you can create execution rules directly using the **Create Rule** dropdown. The dropdown lets you pick which identifier type to use (CDHash, Binary SHA-256, Signing ID, Certificate SHA-256, or Team ID) based on the event's binary. :::tip Hold the **Option** key (macOS) or **Alt** key (Windows/Linux) while clicking a menu item to automatically scope the new rule to the host that generated the event. The dropdown shows a "Scoped to this host" indicator when the key is held. ::: --- ## Filter Language # Filter Language Most Workshop API methods that begin with `List` (e.g., `ListHosts`, `ListRules`, `ListEvents`) support filtering results using a standardized filter language. ## Overview The filter system is: - **Generic**: Works across all API types that support filtering - **Type-safe**: Validates field names and types against the protobuf schema at runtime - **AIP-160 compliant**: Based on [Google's AIP-160 filtering standard](https://google.aip.dev/160) Filters are provided as a string in the `filter` field of List request messages: ```protobuf message ListHostsRequest { int32 page_size = 1; int32 page = 2; string filter = 3; // Filter expression goes here string order_by = 4; } ``` The _fields_ available for filtering will match those in the type returned in the response message. For example, the `ListHostsResponse` message looks like this: ```protobuf message ListHostsResponse { repeated Host hosts = 1; optional bool more = 2; } ``` The fields available for filtering are those on the [`Host`](https://buf.build/northpolesec/workshop-api/docs/main:workshop.v1#workshop.v1.Host) message: ```protobuf message Host { string uuid = 1; string serial = 2; string machine_model = 3; // remaining fields elided for brevity } ``` ## Basic Syntax ### Simple Equality ``` hostname = 'example-host' ``` ### Comparison Operators ``` last_seen_client_mode > 1 last_seen_client_mode >= 1 last_seen_client_mode < 3 last_seen_client_mode <= 3 last_seen_client_mode != 2 ``` ### String Pattern Matching (Case-Insensitive) The `:` operator performs a case-insensitive pattern match (SQL `ILIKE`): ``` hostname:'r%' # Hostnames starting with 'r' hostname:'%dev%' # Hostnames containing 'dev' ``` ## Boolean Logic ### AND Operator ``` hostname = 'homer' AND last_seen_client_mode > 1 ``` ### OR Operator ``` hostname = 'homer' OR hostname = 'marge' ``` ### NOT Operator ``` NOT (hostname = 'homer') ``` ### Operator Precedence AND has higher precedence than OR. Use parentheses to control evaluation order: ``` tags_locked = true AND hostname = 'homer' OR hostname = 'marge' # Evaluates as: (tags_locked = true AND hostname = 'homer') OR (hostname = 'marge') ``` ## Special Values ### Boolean Literals ``` tags_locked = true tags_locked = TRUE # Case-insensitive tags_locked = false tags_locked = FALSE ``` ### NULL Values ``` hostname = NULL # Becomes: hostname IS NULL hostname != NULL # Becomes: hostname IS NOT NULL ``` ### Timestamp Fields Timestamp fields can be compared using Unix timestamps (seconds since epoch): ``` rule_sync_time > 946688400 # After 2000-01-01 01:00:00 UTC ``` ### Current Time Use `NOW()` to get the current timestamp: ``` rule_sync_time > NOW() # Rule sync time is in the future ``` ### Time Arithmetic Use `ADD()` and `SUB()` to perform arithmetic on timestamps: ``` rule_sync_time > SUB(NOW(), 3600) # Within the last hour (3600 seconds) rule_sync_time > SUB(NOW(), 86400) # Within the last 24 hours rule_sync_time < ADD(NOW(), 86400) # Within the next 24 hours ``` These functions take two integer arguments and return the result: | Function | Description | Example | | ----------- | ------------- | ------------------ | | `ADD(a, b)` | Returns a + b | `ADD(NOW(), 3600)` | | `SUB(a, b)` | Returns a - b | `SUB(NOW(), 3600)` | Both arguments must be numeric values (not field references). ## Enum Fields Enum fields can be queried by their string identifier or numeric value: ``` last_seen_client_mode = 'MONITOR' # Using enum identifier last_seen_client_mode = 1 # Using numeric value ``` The filter system validates enum identifiers against the protobuf enum definition at runtime. ## Repeated Fields ### IN Operator with Repeated Fields Check if a value exists within a repeated field: ``` IN('dev', tags) # Check if 'dev' is in the tags array IN('prod', tags) # Check if 'prod' is in the tags array ``` ### IN Operator with Multiple Values Check if a field matches any value in a list: ``` IN(hostname, 'homer', 'marge', 'bart') IN(last_seen_client_mode, 1, 2) IN(last_seen_client_mode, 'LOCKDOWN', 'STANDALONE') ``` ### Counting Array Elements Use `.count` to get the number of elements in a repeated field: ``` tags.count > 0 # Has at least one tag tags.count = 3 # Has exactly 3 tags tags.count < 5 # Has fewer than 5 tags ``` ## Nested Fields For message types that contain nested messages, use dot notation: ``` host.hostname = 'kvothe' host.last_seen_client_mode = 'MONITOR' ``` Example with `IN()` on nested fields: ``` IN(host.hostname, 'kvothe', 'bast') ``` ## Complex Examples ### Multiple Conditions ``` hostname:web% AND tags.count > 0 AND last_seen_client_mode = 'MONITOR' ``` ### Combining OR and AND ``` (hostname = 'web-1' OR hostname = 'web-2') AND tags_locked = false ``` ### Checking for Tagged Production Hosts ``` IN('prod', tags) AND hostname:prod-% ``` ## Type Safety The filter system validates: 1. **Field existence**: Referenced fields must exist in the protobuf message 2. **Field types**: Argument types must match the field type 3. **Enum values**: String enum identifiers must be valid for the enum type 4. **Repeated field operations**: `.count` can only be used on repeated fields ### Common Type Errors ``` # ERROR: Type mismatch (string vs int64) hostname = 42 # ERROR: Type mismatch (enum vs float64) last_seen_client_mode = 3.14 # ERROR: Type mismatch (bool vs int64) tags_locked = 1 # ERROR: Invalid enum identifier last_seen_client_mode = 'INVALID_MODE' # ERROR: .count on non-repeated field hostname.count > 1 ``` ## Error Messages When a filter fails validation, you'll receive an error describing the problem: ``` field "MissingField" referenced in filter does not exist or is not exported ``` ``` type of argument 42 (int64) in filter isn't convertible to type of field "hostname" (StringKind) ``` ``` argument FLARGLE is not valid for the enum santa.sync.v1.ClientMode ``` ## See Also - [AIP-160: Filtering](https://google.aip.dev/160) - Google's API Improvement Proposal for filtering --- ## Hosts # Hosts The Hosts interface provides a comprehensive view of all endpoints running Santa across your organization. This centralized dashboard allows administrators to monitor, manage, and troubleshoot Santa deployments at scale. ## Overview The Hosts dashboard displays information about each endpoint: - **Hostname**: The hostname of the endpoint - **Mode**: Current mode of the Santa agent ([learn more about modes](https://northpole.dev/concepts/mode.html)) - **OS Version**: Operating system version information with indicators for outdated versions - **Santa Version**: Version of the Santa agent - **Last Sync**: Time of last communication with the sync server ## Host Details Clicking on an individual host provides detailed information: - **Host Details**: Hostname, UUID, primary user, hardware model, serial number, OS version/build, and Santa version with update indicators - **Client Mode**: Current enforcement mode (Monitor, Lockdown, or Standalone) with ability to change settings - **Sync Information**: Last sync time, rule sync time, last preflight and postflight times - **Sync Controls**: Options to trigger immediate sync or perform a clean sync - **OS Updates**: Indicators when updates are available for the operating system - **Santa Updates**: Indicators when newer Santa versions are available ## Changing Client Modes To change the mode of a host, click the "Change Mode" button and select the desired mode. :::warning Changing the mode of a host will immediately impact the behavior of the Santa agent on that host. This can cause disruption to users if not done carefully. ::: ## Syncing Hosts Hosts can be synced manually or automatically. - **Manual Sync**: Click the "Sync Now" button to force a sync of the host - **Clean Sync**: Click the "Clean Sync" button to force a clean sync of the host. :::warning A clean sync will take a long time to complete and should only be done when necessary. ::: ## Machine ID Changes If your organization changes the format used for machine IDs (e.g. using the `MachineID` key in Santa), Workshop will attempt to match the new machine ID to an existing host record. If a machine syncs with a new ID but has the same hardware model identifier, serial number, and primary user as an existing host, the existing record will be updated with the new ID rather than creating a duplicate entry. This avoids double-counting hosts in most cases. However, if any of those fields differ — for example, if the primary user changed at the same time as the machine ID — Workshop will treat the endpoint as a new host. The old host entry will remain visible for 30 days before it is automatically removed. During this window the same physical machine will be counted twice. ## Deleting Hosts Hosts can be manually deleted. This will immediately delete all data about this host from the database but will leave events for that host in the events table. --- ## Multi-Party Approval # Multi-Party Approval (MPA) Multi-Party Approval (MPA) adds an additional layer of security by requiring multiple administrators to approve sensitive actions before they are executed. ## Overview When MPA is enabled, the following destructive commands require approval from multiple administrators before they can be executed: - **Kill Process commands** - Commands that terminate processes on hosts - **Disable MPA** - Disabling MPA itself requires multi-party approval - **API Key creation** - Creating an API key with MPA-protected permissions requires approval ## Configuration MPA can be configured in the Workshop Settings page under the "Multi-Party Approval" section. ### Required Approvers Specify how many unique administrators must approve a request before it is executed. This value must be at least 2 to ensure no single administrator can approve their own requests. ### Maximum Duration Set the maximum time an approval request can remain pending before it automatically expires. Expired requests are rejected automatically. ## How It Works 1. An administrator initiates a destructive command (e.g., kill process) 2. Instead of executing immediately, an approval request is created 3. Other administrators can view pending requests and approve or reject them 4. Once the required number of approvals is reached, the command executes 5. If the request expires before receiving enough approvals, it is rejected ## Security Considerations - Administrators cannot approve their own requests - Each administrator can only approve a request once - The requestor is automatically excluded from the approval count - Disabling MPA also requires multi-party approval when MPA is enabled ## API Methods MPA settings can be configured using the following API methods: - `GetMultipartyApprovalSettings` - Retrieve current MPA configuration - `SetMultipartyApprovalSettings` - Update MPA configuration - `DisableMultipartyApproval` - Initiate the process to disable MPA - `ListMultipartyApprovalRequestsForSession` - List pending approval requests - `GetMultipartyApprovalRequest` - Get details of a specific request - `ResolveMultipartyApprovalRequest` - Approve or reject a request --- ## Reports # Reports The Reports interface provides comprehensive insights into your Santa deployment, helping administrators understand security posture, rule effectiveness, and system health across the organization. ## Overview The Reports dashboard offers several analytical views: - **Top Blockables**: Most frequently blocked binaries across your organization - **Dangerous Entitlements**: Binaries with potentially dangerous entitlements - **Ready for Lockdown**: Analysis of hosts that may be ready to transition to Lockdown mode ## Top Blockables The Top Blockables report displays the most frequently blocked binaries across your organization: - **Filename**: Name of the blocked binary - **Signing ID**: The signing identifier of the binary - **CDHash**: The CodeDirectory hash of the binary - **Entitlements**: Any entitlements associated with the binary - **Count**: Number of times the binary has been blocked This report helps identify patterns of blocked applications and potential security risks. ## Dangerous Entitlements The Dangerous Entitlements report highlights binaries that contain potentially risky entitlements: - Displays binaries with entitlements that could pose security risks - Provides detailed information about each entitlement - Helps identify applications that may require additional scrutiny ## Ready for Lockdown TODO --- ## Risk Engine # Risk Engine The Risk Engine empowers security teams to create policies that automatically identify when applications exceed your organization's risk tolerance. You can configure these policies to flag applications for various reasons, such as known malware detection or organization restrictions on virtualization software. The system uses a flexible plugin architechture, allowing multiple plugins to participate in the process of deciding whether a given application is above or below the line of risk. When the Risk Engine is enabled, every time Santa uploads an event to Workshop the binary / application inside the events are passed to it. The Risk Engine then generates an authorization request for each of its plugins in parallel, setting a deadline. If all plugins return an `ALLOW` decision within the deadline then the event is considered safe. If any plugins returns `DENY` or `DENY_MALWARE` then the event is considered dangerous. If any plugins return errors or respond after the deadline they are treated as if the plugin returned a `DENY` decision. ## Configuration ### UI The Risk Engine can be configured in the Settings page. ### API Methods The Risk Engine can be configured using the [UpdateRiskEngineSettings](https://buf.build/northpolesec/workshop-api/docs/main:workshop.v1#workshop.v1.WorkshopService.UpdateRiskEngineSettings) method. ## Internal vs. Remote Plugins Risk Engine plugins come in two flavors - Internal, which are included as part of Workshop and Remote, which are extensions that can be written by customers or North Pole Security. ## Secrets URL Some options in Risk Engine plugins can be configured to use AWS and GCP secret stores. ### AWS 1. Give the Workshop service account read access to the secret The Workshop service account is displayed at the top of the Settings page 2. Pass the ARN to the secret prefixed with `aws://` e.g. `aws://arn:aws:secretsmanager:us-east-1:940000000003:secret:Secret-YYLN9X` ### GCP 1. Give the Workshop service account read access to the secret The Workshop service account is displayed at the top of the Settings page 2. Specify the path to the secret as `gcp://projects//secrets//versions/latest` ## Included Plugins Workshop's internal plugins include: ### VirusTotal The VirusTotal plugin will check the SHA-256 of the binary against VirusTotal using the file report API. The VirusTotal plugin will cache results per user defined parameters. This ensures that results are timely and saves expensive API calls. #### Options - _API Key_ - Your VirusTotal Key; this is either the raw string or a [secrets URL](#secrets-url) - _Cache Time_ - How long in seconds the cache entries should be kept alive for in seconds - _Cache Entries_ - How many entries to cache - _Excluded Engines_ - A list of engines to exclude results from ### Reversing Labs The Reversing Labs plugin will check the SHA-256 hash of a binary against ReversingLabs Spectra file reputation API. If the API deems it malicious it returns a `DENY_MALWARE` response. The ReversingLabs plugin will cache results per user defined parameters. This ensures that results are timely and saves expensive API calls. #### Options - _Username_ - your reversing labs username - _Password_ - your reversing labs username or a secret URL - _Cache Entries_ - How many entries to cache - _Cache Time_ - How long in seconds the cache entries should be kept alive for - _Cache Entries_ - How many entries should be cached (up to 50,000) ### Blockable Rules The Blockable Rules plugin allows you to write rules using the [Common Expression Language](https://cel.dev/) to match properties of a _blockable_. A blockable is a collection of attributes from the binary that Santa or Workshop policy can be matched on. All matchable attributes will be populated into the `blockable` object. If the CEL expression returns true then this plugin will return a `DENY` decision, otherwise it will return an `ALLOW`. You may also use [CEL macros](https://github.com/google/cel-go?tab=readme-ov-file#macros) for working with nested structures such as entitlements. This is an extremely powerful feature that allows you to flag entire classes of software. Furthermore it allows you to tailor an extremely granular policy. For example to have the risk engine return a `DENY` decision for any virtualization software you can use the following rule: ```clike has(blockable.entitlements) && blockable.entitlements.exists(e, e.key == "com.apple.security.hypervisor") || blockable.entitlements.exists(e, e.key == "com.apple.security.virtualization") ``` You can use all of the attributes of a [blockable](https://buf.build/northpolesec/workshop-api/docs/main:workshop.v1#workshop.v1.BinaryBlockable) in rules. This includes: - `sha256` - The SHA-256 of the binary - `cdhash` - The CDHash of the binary - `signing_id` - The signing ID, prefixed with either the team ID or platform - `team_id` - The 10-digit alphanumeric Team ID of the binary that uniquely identifies the publisher - `signed_by` - The certificate chain - `entitlements` - The array of entitlements provided ## Remote Risk Engine Plugins In addition to the included plugins the Risk Engine can be extended via ### Writing Your Own Remote Risk Engine Plugins To write your own remote risk engine plugin you need to simply create a server that takes an HTTP POST with JSON consisting of the `PluginAuthzRequest` and that returns an `RemoteRiskEnginePluginServiceAuthorizeRequest` serialized to JSON. The interaction is essentially as follows: ```mermaid sequenceDiagram Workshop ->> Plugin: Makes an RemoteRiskEnginePluginServiceAuthorizeRequest for a binary Plugin -->> Workshop: Returns a RemoteRiskEnginePluginServiceAuthorizeResponse containing a policy decision ``` :::note Plugin authors are responsible for TLS and authorization. ::: #### Handling Requests The first step is to make a web service that can receive and unmarshal a `PluginAuthzRequest`. ```proto // A RemoteRiskEnginePluginServiceAuthorizeRequest is a request made by Workshop to // a plugin to authorize a binary / blockable. message RemoteRiskEnginePluginServiceAuthorizeRequest { string tx_id = 1; // The transaction ID of the request. BinaryBlockable blockable = 2; // The binary to authorize with all blockable attributes. google.protobuf.Timestamp timestamp = 3; // The timestamp of the request. google.protobuf.Timestamp deadline = 4; // The deadline for the plugin to return a decision before it is automatically considered a denial. } ``` After unmarshaling the `RemoteRiskEnginePluginServiceAuthorizeRequest` you can find all of the details about the binary in the `blockable` field. This contains a subset of the attributes Santa has recorded at the time of execution, including signing information. Each request has a transaction ID (`tx_id`) field and all responses are expected to have the same value in their transaction ID field. Each `RemoteRiskEnginePluginServiceAuthorizeRequest` also contains a `deadline` that the plugin must respond with a `PluginAuthzResponse` before to be considered. Failure to respond within the deadline will be treated as a if the plugin had responded with a deny decision. Once the data from the request has been processed a `RemoteRiskEnginePluginServiceAuthorizeResponse` must be send back to Workshop with a decision and and explanation for the decision. The structure of the `RemoteRiskEnginePluginServiceAuthorizeResponse` is as follows: ```proto // This message is used by a remote risk engine plugin to represent the decision // for a blockable. All errors and timeouts are treated as denials. message RemoteRiskEnginePluginServiceAuthorizeResponse { string tx_id = 1; // The transaction ID of the request this response is for. Decision decision = 2; // The decision for the blockable. Explanation explanation = 3; // An explanation for the decision. string error = 4; // An error message containing any errors the plugin encountered. string plugin_uuid = 5; // The UUID of the plugin that made the decision. google.protobuf.Timestamp good_until = 6; // The time the decision is considered valid until for caching. } ``` Decisions can be one of the following: | Decision | Meaning | | ------------ | --------------------------------------------------------------------- | | UNKNOWN | This is a programming error and should not be used | | DENY | The plugin has determined the binary should be blocked by policy. | | DENY_MALWARE | The plugin has determined the binary is malware and should be blocked | | ALLOW | The plugin believes this binary is safe. | | TIMEOUT | The plugin or something it depends on has timed out | | ERROR | The plugin has encountered an error | All decisions except for allow are considered a denial. See [the Decision proto for more details](https://buf.build/northpolesec/workshop-api/docs/main:workshop.v1#workshop.v1.Decision) Additionally plugin authors are expected to provide an explanation for the decision and optionally a URL for getting more information. Workshop presents this information to to users and also helps with debugging. See [the Explanation proto for more details](https://buf.build/northpolesec/workshop-api/docs/main:workshop.v1#workshop.v1.Explanation) ## Exceptions Risk Engine plugin decisions can be overridden using Exceptions. These are created through the UI or API and grant users in a targeted tag an exception to specific Risk Engine plugin decisions. All exceptions include an expiration date after which they no longer apply. For example, if the Risk Engine's Blockable Rules plugin has a rule banning VPN software e.g. ```clike has(blockable.entitlements) && blockable.entitlements.exists(e, e.key == "com.apple.developer.networking.networkextension" && e.value.contains("packet-tunnel-provider-systemextension")) ``` But you wanted to let members of the tag `vpn-access` approve their own VPN software then you could accomplish this by granting the exception to tag for the specific Blockable Rule. ### Expiry Exceptions all have an expiration date built into them after which point they will not longer be considered. This can be updated via both the UI and API. ### Configuration #### UI Exceptions can be configured via the UI under the Exceptions tab on the Risk Engine Settings portion of the main Settings page. #### API Exceptions can also be mangaged via the [CreateException](https://buf.build/northpolesec/workshop-api/docs/main:workshop.v1#workshop.v1.WorkshopService.CreateException),[UpdateException](https://buf.build/northpolesec/workshop-api/docs/main:workshop.v1#workshop.v1.WorkshopService.UpdateException), [ListExceptions](https://buf.build/northpolesec/workshop-api/docs/main:workshop.v1#workshop.v1.WorkshopService.ListExceptions), and [DeleteException](https://buf.build/northpolesec/workshop-api/docs/main:workshop.v1#workshop.v1.WorkshopService.DeleteException) methods in API --- ## Rules # Rules Workshop provides comprehensive rule management for controlling system behavior across your organization. Rules define policies for both execution control and file access authorization. ## Rule Categories ### Execution Rules Control which binaries can execute on your systems. Execution rules use Santa's binary authorization capabilities to allow or block applications based on cryptographic signatures, certificates, and other identifiers. [Learn more about Execution Rules →](/rules/execution-rules) ### File Access Rules Regulate which processes can read and write files on macOS systems. File Access rules provide fine-grained control over file system access, enabling monitoring, logging, and blocking of access attempts. [Learn more about File Access Rules →](/rules/file-access-rules) --- ## Settings # Settings The Settings interface provides a centralized control panel for configuring Workshop's global settings that affect all hosts in your organization. This dashboard allows administrators to manage default sync settings, removable media blocking behavior, and user access. ## Default Client Mode The Client Mode setting determines the default enforcement behavior for all Santa agents across your organization: - **Monitor**: Allows all executions but logs them for review - **Lockdown**: Only allows executions that match allowlist rules - **Standalone**: Operates without connecting to the sync server Individual hosts can be assigned different modes that override this global default. [Learn more about modes](https://northpole.dev/concepts/mode.html) ## On-Demand Monitor Mode {#on-demand-monitor-mode} On-Demand Monitor Mode allows hosts to temporarily transition into Monitor Mode for a limited duration. This feature is useful when users need to execute applications that would normally be blocked in Lockdown mode, without permanently changing the host's enforcement mode. When enabled, hosts can request temporary Monitor Mode access through the Santa client. The duration of this temporary access is controlled by two settings: - **Max Minutes**: The maximum number of minutes a machine is allowed to transition into Monitor Mode. Valid range: 1-43,200 minutes (1 minute to 30 days). This setting acts as an upper bound for any Monitor Mode request. - **Default Duration Minutes**: The default number of minutes of Monitor Mode granted when requested if no duration is explicitly specified. If set to 0 or not specified, the Max Minutes value is used as the default. This value must not exceed Max Minutes. When On-Demand Monitor Mode is disabled, hosts cannot request temporary Monitor Mode access and must rely on their configured Client Mode setting. ## Removable Media Blocking Removable media blocking controls whether Santa will block the mounting of removable storage devices: - **Disabled**: removable media devices can be mounted normally - **Enabled**: removable media devices will be blocked from mounting - **Enabled with Remount Flags**: removable media devices will be blocked, but can be remounted with specific flags ## Sync Intervals Sync Intervals control how frequently Santa agents communicate with the Workshop server to retrieve updated rules and configurations. - **Full Sync Interval**: Determines how often hosts perform a progressive sync with the server. Valid range: 60-86,400 seconds (1 minute to 24 hours). Default is 600 (10 min). - **Push Notification Full Sync Interval**: When Push Notifications are enabled, this setting determines how often hosts perform a progressive sync with the server. Valid range: 60-86,400 seconds (1 minute to 24 hours). Default is 14400 (4 hours). ## CEL Fallback Rules CEL Fallback Rules allow you to define expression-based policies that are evaluated when no other rule matches a binary. They can be used to implement broad organizational policies — such as blocking binaries with specific entitlements or requiring Touch ID for unsigned software — while still allowing standard rules to take priority for specific applications. CEL Fallback Rules are configured per-tag in Sync Settings and require Santa 2026.3 or later. For full details on CEL expression syntax, available input variables, and return values, see [CEL Policy Rules](/rules/execution-rules#cel-fallback-rules). ## Telemetry Filter Expressions Telemetry Filter Expressions are CEL expressions evaluated by Santa on the client to drop or redact events before they are uploaded to your telemetry bucket. They are configured per-tag in Sync Settings under the **Telemetry** tab. For syntax, available variables, and worked examples, see [Telemetry Filter Expressions](/telemetry/filter-expressions). ## Santa Auth Workshop supports two methods for authenticating Santa clients. Changes to authentication methods will affect the generated config shown on the Santa tab. If both Token and mTLS authentication are enabled, the config will use the mTLS configuration. ### Token Authentication Token-based authentication allows Santa clients to authenticate using bearer tokens. You can manage authentication tokens from the Settings interface: - **Multiple Tokens**: Create and manage multiple authentication tokens for different deployments or environments - **Last Used Tracking**: Each token displays the timestamp of its last use, helping you identify active and inactive tokens - **Token Deletion**: Tokens can be deleted when they are no longer needed, immediately revoking access for any clients using that token When using token authentication, Santa clients connect to the standard `SyncBaseURL` (e.g., `https://tenant.workshop.cloud/santa`). ### mTLS Authentication Mutual TLS (mTLS) authentication provides certificate-based authentication for enhanced security. Workshop supports configuring multiple Certificate Authority (CA) certificates from the Settings interface, allowing you to manage certificates for different organizational units or for seamless transition between issuing CAs. When mTLS is enabled, the `SyncBaseURL` key in Santa's configuration will include an `mtls.` prefix (e.g., `https://mtls.tenant.workshop.cloud/santa`). This special URL only works when mTLS authentication is properly configured on both the Workshop server and the Santa client. **Important**: The mTLS-prefixed URL will only accept connections from clients presenting valid certificates signed by one of the configured CA certificates. Standard token-based authentication will not work with the mTLS URL. ## User Management Workshop has two distinct user systems that serve different purposes: 1. **SSO Users**: Users who can log into the Workshop web interface 2. **Directory Users & Groups**: Users and groups that represent your organization's identity structure for policy assignment ### SSO Users SSO Users are those who can access the Workshop web interface. These users authenticate via Single Sign-On (SSO) through your identity provider. From the Settings page, you can: - **Configure SSO**: Set up your identity provider connection - **Verify Domains**: Confirm ownership of your organization's email domains - **Manage Users**: View and manage users who can log into Workshop - **Assign Roles**: Control permissions by assigning roles to SSO users ### Directory Users & Groups Directory Users and Groups represent your organization's identity structure. When a host reports its [primary user](https://northpole.dev/configuration/keys#MachineOwner), Workshop looks up that user in the directory to determine which groups they belong to. Groups can have [tags](./tags) assigned to them, which are then automatically applied to the host. This enables powerful policy automation: instead of manually tagging each host, you can assign tags to groups in your directory, and hosts will automatically inherit the correct tags based on their primary user. ### Directory Type Workshop supports two modes for managing Directory Users and Groups: #### Directory Sync (DSYNC) In DSYNC mode, users and groups are automatically synchronized from an external directory service via SCIM. **Advantages:** - Users and groups stay in sync with your identity provider(IdP)'s external directory automatically - No manual maintenance required - Changes in your identity provider(IdP)'s external directory are reflected in Workshop - Leverage existing group structures for policy assignment **Configuration:** 1. Set the Directory Type to "Directory Sync" 2. Click "Configure Directory Sync" to set up the SCIM connection 3. Use "Trigger Directory Sync" to force an immediate sync #### Local Directory In Local mode, users and groups are created and managed manually within Workshop. **Advantages:** - No external directory service required - Full control over user and group definitions - Useful for testing or organizations without centralized identity management - Can define groups that don't exist in your identity provider(IdP)'s external directory **Configuration:** 1. Set the Directory Type to "Local" 2. Create users and groups directly in the Users and Groups tabs or via [the API](https://buf.build/northpolesec/workshop-api/docs/main:workshop.v1#workshop.v1.WorkshopService.CreateUser) 3. Manually assign users to groups as needed or via [the API](https://buf.build/northpolesec/workshop-api/docs/main:workshop.v1#workshop.v1.WorkshopService.AddUserToGroup) :::warning Changing the directory type will delete all existing users and groups of the current type. This action cannot be undone. ::: ### Assigning Groups to Hosts There are two ways hosts can be associated with groups: #### Via Primary User When using Directory Sync, Workshop automatically looks up the host's primary user in the directory and applies tags from any groups that user belongs to. This happens during each Santa sync. For example, if user `alice@example.com` is the primary user of a MacBook and she's a member of the "Engineering" group in your IdP's external directory, the MacBook will automatically receive any tags assigned to the "Engineering" group in Workshop. #### Via Primary User Groups (Client-Defined) Starting with Santa **version 2025.6**, you can define [primary user groups](https://northpole.dev/configuration/keys#MachineOwnerGroups) directly in the Santa configuration. Workshop will look up these group names in the directory and apply their tags, even if the primary user isn't a member of those groups in your IdP's external directory. See [Tags](./tags) for more details on how group membership affects tag assignment. ## Slack See [Slack Settings](./slack) for more information. ## Workshop Updates Workshop provides flexible update management to keep your server current with the latest features, improvements, and security patches. You can choose between automatic updates with configurable policies or manual updates triggered on-demand. ### Update Process Workshop's update mechanism is designed to be seamless and zero-downtime. When an update is triggered (either automatically or manually), the current version continues serving traffic during the update. Once the update completes, the new version automatically takes over. This ensures continuous availability throughout the update process. All update activities are recorded in the audit log for compliance and troubleshooting purposes, whether triggered automatically by the system or manually by a user. :::note While the update process is seamless there can be a short period during the update where the web UI may attempt to partially load both old and new versions, causing errors. If this happens, wait a few minutes and refresh the page. This has no impact on Santa client syncing or API use. ::: ### Manual Updates You can manually trigger updates at any time, regardless of automatic update settings: 1. Navigate to **Settings** → **Administration** in the Workshop interface 2. In the **Updates** section, view the current version and available updates 3. If updates are available, select the desired version from the dropdown 4. Click **Trigger Update** to install the selected version immediately Manual updates are not restricted by automatic update modes or time windows, giving you full control to update on your schedule. The triggering user is recorded as the actor in audit logs for manual updates. ### Automatic Updates Workshop can automatically install updates based on policies you define, eliminating the need for manual intervention while maintaining control over when and what gets updated. #### Automatic Update Modes Workshop provides three automatic update modes to match your organization's policies: - **Disabled**: Automatic updates are turned off. All updates must be triggered manually through the Workshop interface or API. - **All Updates**: Automatically installs all available Workshop updates as soon as they're released. Use this mode to stay current with the latest features and improvements. - **Security Updates Only** (Default): Automatically installs only updates that contain security fixes. Feature releases and other non-security updates are skipped. This mode is recommended for production environments that prioritize stability while ensuring critical security patches are applied promptly. #### Update Scheduling When automatic updates are enabled, Workshop checks for updates every hour and installs them immediately if an update matching your configured mode is available. You can optionally restrict when automatic updates are installed by configuring a time window. **Update Window Configuration:** - **Any Time** (Default): Updates can install during any hour. No restrictions are applied. - **Specific Hours**: Define a start hour and end hour (in your local timezone) to restrict automatic updates to a particular window. For example, configure updates to only install between 2:00 AM and 6:00 AM to minimize disruption during business hours. - **Overnight Window**: If the start hour is later than the end hour (e.g., 10:00 PM to 6:00 AM), the window wraps around midnight. This is useful for scheduling updates during off-hours. :::note Time windows are stored in UTC internally but displayed in your browser's local timezone for convenience. The system checks for available updates every hour, and if an update matching your mode is available and the current hour falls within your configured window, the update will be installed automatically. ::: #### Configuring Automatic Updates To configure automatic update settings: 1. Navigate to **Settings** → **Administration** in the Workshop interface 2. Scroll to the **Automatic Updates** section 3. Select your preferred automatic update mode 4. Optionally configure a time window to restrict when automatic updates can install 5. Click **Save Settings** to apply your changes Changes to automatic update settings are recorded in the audit log. --- ## Slack # Slack Bot Included with Workshop is a Slack chat bot that can help users go through an approvals workflow in Slack. ## Configuring Slack In order to use the Slack Bot integration you must have permissions to generate a [Configuration token](https://api.slack.com/concepts/token-types#config) in your Slack workspace. - Follow the instructions at https://api.slack.com/reference/manifests#config-tokens to create a configuration token. For an Slack admin user this is usually at the bottom of [https://api.slack.com/apps/](https://api.slack.com/apps/) - In Workshop, go to the Settings page and scroll down to the Slack settings card, then click the "Initialize Slack Bot" button - In the modal paste the configuration token from step one and click Submit - Click Close - Open [https://api.slack.com/apps](https://api.slack.com/apps) and select the Workshop App. - Customize it to your liking, including icon or change the name of the bot. - Install the app into your Slack workspace by selecting Settings > Install App and clicking the button - Collect the Slack Bot token and Signing Secret. ## Configuring Workshop Next you need to configure Workshop's bot to use the new Slack app. In Workshop start by going to the Settings page and scrolling down until you see the Slack Settings card. ### Fill in your Slack Workspace Name This is the portion of your Slack workspace domain name before the `slack.com`, portion. For example if your workspace is `example.slack.com` then your workspace for workshop should be listed as `example` ### Fill in your Slack Bot token in the Slack Token field You can specify the slack bot token from step 7 of the previous section to store the token in the database. This field also supports using the AWS and GCP secret stores. If you are using AWS, you can use SecretManager by doing the following: - Give the Workshop service account read access to the secret - The Workshop service account is displayed at the top of the Settings page - Pass the ARN to the secret prefixed with a `aws://` e.g. `aws://arn:aws:secretsmanager:us-east-1:940000000003:secret:SlackSecret-YYLN9X` If you are using GCP, you can use SecretsManager by doing the following: - Give the Workshop service account read access to the secret - The Workshop service account is displayed at the top of the Settings page - Specify the path to the secret as `gcp://projects/projectID/secrets/secretID/versions/latest` ### Fill in your HMAC secret The HMAC secrete ensures that Workshop will only receive traffic from Slack The HMAC secret is the signing secret from the previous Slack section. Simply cut and paste this here. Additionally this can also use the secret stores just like the slack token in the previous section ### Save your slackbot settings Simply save your settings using the save settings button. ## Required Scopes The Workshop Slack app requires the following OAuth scopes. These are automatically configured if you use the manifest-based installation flow. ### Bot Token Scopes | Scope | Purpose | | ------------------ | ---------------------------------------------------------------- | | `channels:join` | Join designated approval channels to post notifications | | `channels:read` | List available channels for configuration | | `channels:history` | Update approval messages in public channels | | `chat:write` | Send approval and notification messages | | `groups:read` | List private channels the bot has been added to | | `groups:history` | Update approval messages in private channels | | `groups:write` | Create and post in group conversations for approver workflows | | `im:read` | List direct message conversations | | `im:write` | Send direct messages to users for approval notifications | | `im:history` | Update approval messages in direct messages | | `mpim:read` | List group chats created for social voting workflows | | `mpim:history` | Update approval messages in social voting group chats | | `mpim:write` | Create group chats and post messages for social voting workflows | | `users:read` | Look up user information for approval workflows | | `users:read.email` | Map users by email address for designated approver workflows | ### User Token Scopes | Scope | Purpose | | ---------------- | ------------------------------------------ | | `identity.email` | Read the user's email for identity mapping | | `openid` | OpenID Connect authentication | ## Configure Your Approvals Workflow to use Slack Notifications The Slack bot will only send messages when all of the following are true: - Santa has blocked an application from running on a users system - The user is running in [Lockdown mode](https://northpole.dev/features/binary-authorization/#client-mode) and does not have an explicit rule allowing it - The user is part of a tag that has approval workflows configured to use Slack notifications ## Configuring via the API All of the above steps aside from the Slack portions can be accomplished using the `InstallChatBot` and `UpdateChatSettings` API methods. Additionally these settings can be saved using the `GetChatSettings` API. All methods require the `settings:write` and `settings:read` permissions. ## Configuring Santa (optional) By default Santa will redirect users to the web based approvals workflows. If you want users to go directly to the Slack message when an application is blocked you can specify an [EventDetail](https://northpole.dev/configuration/keys/#EventDetailURL) of `https://.workshop.cloud/slack/details/%machine_id%/%file_identifier%` to have the open button in the Santa modal direct users to Slack. --- ## Tags # Tags Tags are a flexible mechanism for assigning rules and settings to hosts. ## Concepts - **Tag** - a named collection of settings and rules applied to a set of hosts. By default there's always a **global** tag which applies to all hosts connected to Workshop and each host gets a tag defined as **host:\**. - **Tag Order** - the order in which tags inherited by groups are applied to hosts. This defines the precedence used when resolving settings and rules. - **Overridden Tags** - when an admin explicitly sets the tags on a host via the "Override tags" toggle. This locks the tag set on the host so it is no longer derived from group membership. Hosts with overridden tags cannot participate in approval workflows. - **Sync Settings** - options that change how Santa operates on a given host. When applied to tags they are resolved from least specific to most specific tag. Each individual setting is replaced atomically by a higher-precedence tag — values are never merged within a single setting. For example, a device cannot combine Allowed Path Regex values from multiple tags; only the highest-precedence tag's value applies. This means different settings can come from different tags (e.g. one tag sets Client Mode, another sets Blocked Path Regex), but any single setting is wholly owned by the most specific tag that defines it. Settings that follow this atomic override behavior include: - Client Mode - Removable Media Blocking - Network Share Blocking - Transitive Rules - Allowed Path Regex - Blocked Path Regex - Telemetry Collection - Network Extension - **Approval Workflow Settings** - these define if the hosts running Santa that are part of the tag can use approval workflows to allow applications that don't have explicit rules. The Approval Workflow settings are an atomic unit (not merged from less specific tags). Simply, the most specific tag's value applies. They do not apply to hosts with overridden tags. - **Risk Engine Exceptions** - settings that allow a Santa instance that's a member of a tag to override a risk engine plugin. - **Rules** - policies that describe what software Santa should allow or block. Rules from multiple tags can apply to the same host. When tags define conflicting rules for the same identifier, the most specific / highest precedence tag wins. Admins can apply rules, approval workflow settings and sync settings to tags via the APIs. These won't apply until the tag becomes part of a host's effective tag set, either directly or through group membership. ### Relationships ```mermaid graph LR PU[Primary Users] -->|have one or more| H[Hosts] PU -->|are a member of one or more| G[Groups] H -->|have multiple| T[Tags] G -->|are mapped to one or more| T T -->|have| SS[Sync Settings] T -->|have| AWS[Approval Workflow Settings] T -->|have| R[Rules] T -->|have| REE[Risk Engine Exceptions] ``` ### How Settings and Rules Are Applied Settings and rules are resolved from global (least specific) to the host tag (most specific). When multiple tags define the same setting or a conflicting rule, the setting or rule from the most specific tag wins. Note: Approval workflow settings have additional host-assignment restrictions described above. ```mermaid graph LR A[Global] --> B[Lowest Precedence Tag] --> C[Highest Precedence Tag] --> D["host:<machine_uuid>"] ``` Consider a host with the effective tag order: **global → team-a → host:\** that was applied via group membership. | Setting Type | global | team-a | host:\ | Effective Value | | ---------------------- | ----------- | ------------- | ------------- | --------------- | | **Client Mode** | Monitor | Lockdown | _(not set)_ | Lockdown | | **Blocked Path Regex** | `/tmp/.*` | `/opt/.*` | _(not set)_ | `/opt/.*` | | **Approval Workflow** | _(not set)_ | Self-approval | _(not set)_ | Self-approval | | **Rule for app X** | Block | Allow | _(not set)_ | Allow | | **Rule for app Y** | _(not set)_ | Block | Allow | Allow | ## Tag Creation **Important:** Tags must be created before they can be assigned. You cannot reference a tag that doesn't already exist. To create tags: 1. Navigate to the All Tags section in /settings. 2. Click "Create Tag" 3. Provide a unique name - max 42 chars - then click "Next" 4. You will be prompted to add the tag to the tag order. Drag the tag to the desired position and click "Create". Once created the tag will appear in the Tagged Settings pane above and can now be assigned to rules, settings, and hosts. The tag order can also be edited in the Tagged Settings pane. An unlimited number of tags can be created, but only 25 can be in the tag order at a time. If this limit is restrictive, reach out to support with your use case. Tags can also be created using the [CreateTag](https://buf.build/northpolesec/workshop-api/docs/main:workshop.v1#workshop.v1.WorkshopService.CreateTag) RPC. ## Tag Assignment Methods Tags can be applied to hosts via two methods: ### 1. Manual Assignment (Per-Host) Tags can be manually assigned directly to individual hosts through the Workshop interface or [UpdateHost](https://buf.build/northpolesec/workshop-api/docs/main:workshop.v1#workshop.v1.WorkshopService.UpdateHost) RPC. This provides granular control over which tags are applied to specific machines. :::warning If a host has tags manually assigned, that host cannot participate in approval workflows. ::: ### 2. Automatic Assignment (Via Groups) Tags can be automatically applied to hosts through group membership. This provides scalable tag management across multiple hosts. In order to use tags applied by groups, they must be added to the Tagged Settings section. This defines the order in which the tags will apply. 1. Navigate to the Tagged Settings section in /settings. 2. Click "Add Settings" 3. Choose a tag 4. Click Add There is currently a 25 tag limit. If you find you need more tags, contact support with your use case. Tags can also be ordered with the [UpdateTagOrder](https://buf.build/northpolesec/workshop-api/docs/main:workshop.v1#workshop.v1.WorkshopService.UpdateTagOrder) RPC. ## Group Attachment Methods There are two methods to attach a host to a group (which contains tags): ### Method 1: Directory Sync When directory sync is enabled: - Any group the host's [primary user](https://northpole.dev/configuration/keys#MachineOwner) is a member of will gain access to that group's assigned tags - This provides seamless integration with existing directory structures. - Tag assignment happens automatically based on user group membership ### Method 2: Primary User Groups (Client-Defined) Starting with **version 2025.6** of Santa: - You can define [primary user groups](https://northpole.dev/configuration/keys#MachineOwnerGroups) for a host directly on the client - This method provides more flexibility for environments that aren't using directory sync - Allows manual specification of which groups a host should inherit tags from :::warning If a host has tags manually assigned via primary user groups, that host cannot participate in approval workflows. This is a temporary restriction, future versions of Workshop will enable approval workflows for client defined groups. ::: ### Tag Application Order For both group assignment methods, the total set of tags are applied to the host using the defined tag order. This ensures consistent and predictable tag application across your environment. ## Examples ### Roll Out Lockdown Mode to Sales First An admin wants to move from Monitor mode to Lockdown mode across the fleet but wants to start with Sales (who run a smaller, more predictable set of software) before rolling it out company-wide. 1. Create a **sales-lockdown** tag 2. Map it to the existing Sales IdP group 3. Place **sales-lockdown** above **global** in the tag order 4. Set Client Mode to Lockdown on the **sales-lockdown** tag (global stays in Monitor) 5. Monitor for a week, resolve any issues 6. When confident, change the **global** tag to Lockdown mode and remove the **sales-lockdown** tag ### Different Approval Workflows per Department A company wants Engineering to self-approve low-risk software via the risk engine, while Sales and Finance require manager approval for any new software. 1. Create **engineering**, **sales**, and **finance** tags mapped to their respective IdP groups 2. On the **engineering** tag, configure approval workflows to allow self-approval when the risk engine score is below the threshold 3. On the **sales** and **finance** tags, configure approval workflows to require manager approval for all new software 4. Place all three tags above **global** in the tag order Result: each department gets a tailored approval experience without manual per-host configuration. If a user is a member of multiple groups, the associated tag with the higher precedence wins. ### Incremental Ban of Compromised Software A vulnerability is announced for a widely-used application. The admin wants to block it but roll out the ban gradually to minimize business disruption. 1. Create a **ban-rollout** tag 2. Create an IdP group mapped to that tag 3. Place it above **global** in the tag order 4. Add a block rule for the application to the **ban-rollout** tag 5. Add a small initial set of users to the IdP group 6. (Optional) Trigger a sync 7. Keep adding members incrementally, waiting for issues to surface 8. Once at 100%, move the block rule to **global** and delete the **ban-rollout** tag --- ## Telemetry # Telemetry Workshop provides powerful telemetry capabilities for analyzing Santa security events. The telemetry system integrates with [Santa's telemetry collection](https://northpole.dev/features/telemetry/) to store and enable querying of detailed endpoint activity data in cloud storage buckets. :::info Telemetry is not enabled by default in Workshop. To enable telemetry collection and cloud storage integration, please contact North Pole Security support for configuration assistance. ::: ## Querying Telemetry ### Table Naming Convention Workshop uses dynamic table names: | Table Format | Description | Example | | --------------------------- | --------------------------- | ----------------------------- | | `_YYYY` | All events of type for year | `execution_2025` | | `_YYYYMM` | Events for specific month | `execution_202501` | | `_YYYYMMDD` | Events for specific day | `execution_20250125` | | `_YYYY_` | Host-specific events | `execution_2025_a1b2c3d4` | | `_YYYYMMDD_` | Host & date specific | `execution_20250125_a1b2c3d4` | **Event Types**: `execution`, `fork`, `close`, `file_access`, etc. For complete details on event types and their data, see the [Schema](/telemetry/schema) page. :::warning Host ID Format When using host UUIDs with dashes in table names, replace dashes with underscores (e.g., `a1b2c3d4-e5f6-g7h8` becomes `a1b2c3d4_e5f6_g7h8`) to avoid SQL syntax errors. ::: ### SQL Examples ```sql -- Count total execution events this year SELECT COUNT(*) FROM execution_2025; -- Recent execution events for a specific host SELECT * FROM execution_20250125_a1b2c3d4_e5f6_g7h8 LIMIT 10; -- Execution events for a specific binary SELECT * FROM execution_20250125 WHERE Target.Executable.Hash.Hash = 'sha256-hash-here' LIMIT 10; -- Find processes with dangerous entitlements SELECT EventTime, Hostname, Instigator.Executable.Path FROM execution_20250125 WHERE list_contains( list_transform(EntitlementInfo.Entitlements, x -> x.Key), 'com.apple.security.cs.allow-jit' ) LIMIT 10; -- Processes with specific environment variables SELECT * FROM execution_20250125 WHERE list_contains( list_transform(Envs, x -> starts_with(x, 'HOMEBREW_PREFIX=')), true ) LIMIT 10; ``` ## Filtering Telemetry on the Client Hosts can run CEL expressions to drop or redact events before they are uploaded to your bucket. This is useful for reducing volume, excluding noisy event types, or scrubbing sensitive values like tokens out of events. See [Filter Expressions](/telemetry/filter-expressions) for details. ## Additional Resources For detailed information about all event types and their complete schemas, see the [Schema](/telemetry/schema) page. For more information about Santa's telemetry capabilities, visit [Santa's telemetry documentation](https://northpole.dev/features/telemetry/). --- ## Execution Rules # Execution Rules The Execution Rules interface provides a comprehensive view of all rules that control execution of binaries across your organization. This centralized dashboard allows administrators to create, monitor, and manage Santa execution rules at scale. ## Overview The Execution Rules dashboard displays information about each rule: - **Identifier**: The unique identifier for the rule (hash, certificate, etc.) - **Comment**: Description or purpose of the rule - **Rule Type**: Type of rule (Binary, Certificate, Team ID, Signing ID, CDHash) - **Policy**: Action to take when the rule is matched (Allowlist, Blocklist, etc.) ## Rule Types Santa supports several types of execution rules: - **Binary**: Rules based on the cryptographic hash of a binary - **Certificate**: Rules based on the code signing certificate - **Team ID**: Rules based on Apple Developer Team IDs - **Signing ID**: Rules based on signing information - **CDHash**: Rules based on the CodeDirectory hash of a binary ## Rule Policies Each rule can be configured with one of the following policies: - **Allowlist**: Explicitly allow execution - **Allowlist Compiler**: Special rule for compiler processes - **Blocklist (Malicious)**: Block execution due to malicious content - **Blocklist (Policy)**: Block execution due to policy violation - **Silent Blocklist (Malicious)**: Block without notification due to malicious content - **Silent Blocklist (Policy)**: Block without notification due to policy violation ## Creating Rules To create a new rule, click the "Create Rule" button and fill in the required information: - Rule type - Identifier (hash, certificate, etc.) - Policy action - Optional comment to describe the rule's purpose ### Scoping Rules to Tags Rules can optionally be scoped to one or more tags. When creating a rule, use the tag selector to pick which tags the rule applies to. If no tag is selected, the rule applies globally. To scope a rule to a specific host, check **Show host tags** and search by hostname or primary user — you don't need to know the host UUID. :::warning Rules take effect immediately after creation. Ensure you've verified the identifier before creating a rule. ::: ## Rule Deployment Once created, rules are automatically distributed to Santa clients during their next sync. The timing depends on your sync server configuration. For more detailed information about Santa rules and configuration options, visit the [Santa Documentation](https://northpole.dev/deployment/configuration.html). ## CEL Policy Rules CEL (Common Expression Language) rules provide a flexible, expression-based way to make execution decisions in Santa. Unlike traditional rules that match a single identifier (hash, certificate, etc.), CEL rules evaluate an expression against properties of the binary and its execution context to determine whether to allow, block, or require additional authorization. CEL rules are evaluated by the Santa agent on-device at execution time. Workshop validates CEL expressions before they are saved, ensuring they compile and return a valid decision type. :::info You can test CEL expressions interactively using the [CEL Playground](https://northpole.dev/cookbook/cel-playground) before deploying them as rules. ::: ### Creating CEL Rules CEL rules are created like other execution rules but with the policy set to **CEL** and a CEL expression provided. The expression is validated at creation time and must: - Compile successfully against the CEL environment - Return either a **boolean** or a valid **return value** (see below) - Not reference `UNSPECIFIED` (only fallback rules may use `UNSPECIFIED`) CEL rules can be scoped to tags just like other execution rules. ### Input Variables CEL expressions have access to the following variables, which represent properties of the binary being executed and its runtime context. #### `target` — Executable File Properties The `target` variable contains static properties of the executable file. Rules that only use `target` fields produce **cacheable** results, meaning Santa can remember the decision and skip re-evaluation for subsequent executions of the same binary. | Field | Type | Description | | ---------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------- | | `target.signing_id` | string | The signing ID of the binary, if validly signed | | `target.team_id` | string | The Apple Developer Team ID, if validly signed | | `target.signing_time` | timestamp | Timestamp of when the binary was signed (set by the developer, not verified) | | `target.secure_signing_time` | timestamp | Timestamp certified by Apple's timestamp authority (trustworthy) | | `target.is_platform_binary` | bool | Whether this is an Apple platform binary | | `target.entitlements` | map(string, string) | The binary's entitlements. Values are JSON strings — use `"true"` or `"false"` for boolean entitlements | #### `path` — Executable Path | Field | Type | Description | | ------ | ------ | ---------------------------------------- | | `path` | string | The full resolved path of the executable | Requires Santa 2026.3+. Using this field makes the result **non-cacheable**. #### `args` — Command-Line Arguments | Field | Type | Description | | ------ | ------------ | ---------------------------------------------------- | | `args` | list(string) | The command-line arguments passed to the new process | Using this field makes the result **non-cacheable**. #### `envs` — Environment Variables | Field | Type | Description | | ------ | ------------------- | --------------------------------------------------- | | `envs` | map(string, string) | The environment variables passed to the new process | Using this field makes the result **non-cacheable**. :::info For environment variable keys that contain periods (e.g. `com.apple.dt.Xcode.SourcePathRemapping`), use the `in` operator or bracket syntax instead of the `has()` macro: ```cel // ✅ Correct 'com.apple.dt.Xcode.SourcePathRemapping' in envs envs['com.apple.dt.Xcode.SourcePathRemapping'] == '5' // ❌ Will not compile has(envs.com.apple.dt.Xcode.SourcePathRemapping) ``` ::: #### `euid` — Effective User ID | Field | Type | Description | | ------ | ---- | ------------------------------------------- | | `euid` | int | The effective user ID of the executing user | Requires Santa 2025.12+. Using this field makes the result **non-cacheable**. #### `cwd` — Current Working Directory | Field | Type | Description | | ----- | ------ | ----------------------------------------------------------- | | `cwd` | string | The current working directory of the process being executed | Requires Santa 2025.12+. Using this field makes the result **non-cacheable**. #### `ancestors` — Process Ancestry | Field | Type | Description | | ----------- | -------------- | ------------------------------------------------------------------------------------------------------------ | | `ancestors` | list(Ancestor) | The process ancestry chain. The first element is the immediate parent, followed by its parent, up to launchd | Each `Ancestor` has the following fields: | Field | Type | Description | | ------------ | ------------ | --------------------------------------------------- | | `path` | string | The executable path of the ancestor process | | `signing_id` | string | The signing ID, if validly signed | | `team_id` | string | The Team ID, if validly signed | | `cdhash` | string | The CDHash, if validly signed | | `args` | list(string) | The command-line arguments for the ancestor process | Requires Santa 2026.2+. Using this field makes the result **non-cacheable**. #### `fds` — Open File Descriptors | Field | Type | Description | | ----- | -------------------- | ----------------------------------------------------- | | `fds` | list(FileDescriptor) | The open file descriptors associated with the process | Each `FileDescriptor` has the following fields: | Field | Type | Description | | ------ | ------ | ------------------------------- | | `fd` | int | The file descriptor number | | `type` | FDType | The type of the file descriptor | Available `FDType` values: `FD_TYPE_UNKNOWN`, `FD_TYPE_ATALK`, `FD_TYPE_VNODE`, `FD_TYPE_SOCKET`, `FD_TYPE_PSHM`, `FD_TYPE_PSEM`, `FD_TYPE_KQUEUE`, `FD_TYPE_PIPE`, `FD_TYPE_FSEVENTS`, `FD_TYPE_NETPOLICY`, `FD_TYPE_CHANNEL`, `FD_TYPE_NEXUS` Requires Santa 2026.3+. Using this field makes the result **non-cacheable**. ### Return Values CEL expressions must return either a **boolean** or a **return value enum**. #### Boolean Returns | Value | Decision | | ------- | ------------------------------------------- | | `true` | Equivalent to `ALLOWLIST` — allow execution | | `false` | Equivalent to `BLOCKLIST` — block execution | #### Return Value Enum These values can be used directly as keywords in CEL expressions: | Value | Description | | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | `ALLOWLIST` | Allow the process to execute | | `ALLOWLIST_COMPILER` | Allow execution and, if transitive allowlisting is enabled, locally allowlist any files created by this binary | | `BLOCKLIST` | Block the process from executing | | `SILENT_BLOCKLIST` | Block execution without showing a GUI notification to the user | | `REQUIRE_TOUCHID` | Hold execution until the user authorizes via biometrics (or password if `EnableStandalonePasswordFallback` is enabled). Requires Santa 2026.1+ | | `REQUIRE_TOUCHID_ONLY` | Like `REQUIRE_TOUCHID` but skips the Santa block dialog and goes straight to TouchID authorization. Requires Santa 2026.1+ | :::warning `SILENT_BLOCKLIST` should be used sparingly — silently blocked applications can be very confusing for users. ::: #### TouchID with Cooldown Instead of returning `REQUIRE_TOUCHID` or `REQUIRE_TOUCHID_ONLY` directly, use the cooldown functions to specify how long the authorization should remain valid: ```cel require_touchid_with_cooldown_minutes(5) require_touchid_only_with_cooldown_minutes(10) ``` The cooldown value (in minutes) controls how long after a successful authorization the user can re-execute the binary without being prompted again. TouchID return values are always **non-cacheable**. Requires Santa 2026.1+. ### Cacheability Santa can cache the result of a CEL expression to avoid re-evaluating it on every execution of the same binary. A result is **cacheable** only when the expression exclusively uses static `target` fields. Using any of the following makes the result **non-cacheable**: - `args`, `envs`, `euid`, `cwd`, `ancestors`, `path`, `fds` - `REQUIRE_TOUCHID`, `REQUIRE_TOUCHID_ONLY`, or the cooldown functions Workshop displays a cacheability indicator when creating or editing CEL rules so you can understand the performance implications of your expression. ### CEL Fallback Rules In addition to standard CEL rules, CEL expressions can be configured as **fallback rules** in Sync Settings. Fallback rules are evaluated when no other rule matches and can optionally return `UNSPECIFIED` to pass the decision to the next fallback rule in the chain. CEL fallback rules require Santa 2026.3 or later. Key differences from standard CEL rules: - Configured per-tag in Sync Settings (not as standalone rules) - May return `UNSPECIFIED` to defer the decision - Maximum of 10 fallback rules per sync setting - Can include a custom message and URL shown to the user on block ### Minimum Santa Version Requirements Workshop automatically calculates the minimum Santa version required for a CEL rule based on the variables and functions it uses: | Feature | Minimum Version | | ------------------------------------------------------------- | --------------- | | Basic CEL (target fields only) | No minimum | | `euid`, `cwd` | Santa 2025.12+ | | `REQUIRE_TOUCHID`, `REQUIRE_TOUCHID_ONLY`, cooldown functions | Santa 2026.1+ | | `ancestors` | Santa 2026.2+ | | `path`, `fds` | Santa 2026.3+ | Hosts running older versions of Santa will not evaluate rules that use features they don't support. ### Available Operations CEL expressions support standard CEL operations including (see [celbyexample.com](https://celbyexample.com) for a full reference): - **Comparison**: `==`, `!=`, `<`, `>`, `<=`, `>=` - **Logical**: `&&`, `||`, `!` - **Arithmetic**: `+`, `-`, `*`, `/`, `%` - **Ternary**: `condition ? value_if_true : value_if_false` - **Membership**: `in` - **String functions**: `startsWith()`, `endsWith()`, `contains()`, `join()`, `size()` - **List macros**: `exists()`, `all()`, `filter()`, `map()`, `size()` - **Optional field access**: `has()` - **Timestamp**: `timestamp()` for constructing timestamps for comparison ### CEL Examples #### Allow binaries signed after a specific date ```cel target.secure_signing_time > timestamp('2025-01-01T00:00:00Z') ``` #### Block a binary when run with a specific argument ```cel '--inspect' in args ? BLOCKLIST : ALLOWLIST ``` #### Allow only when launched from a specific parent process ```cel ancestors.exists(a, a.signing_id == 'platform:com.apple.Terminal') ``` #### Block when running as root from a non-standard directory ```cel euid == 0 && !cwd.startsWith('/Applications') ? BLOCKLIST : ALLOWLIST ``` #### Block hypervisors ```cel target.entitlements.exists(k, k == 'com.apple.security.hypervisor' || k == 'com.apple.security.virtualization') ? BLOCKLIST : UNSPECIFIED ``` This example uses `UNSPECIFIED` and is only valid as a **fallback rule**. To create exclusions, allowlist a specific virtualization tool by Team ID or Signing ID — the allowlist rule will take priority over this fallback rule. #### Check for specific file descriptor types ```cel fds.exists(f, f.type == FD_TYPE_SOCKET) ? BLOCKLIST : ALLOWLIST ``` #### Check environment variables with fallback ```cel 'DEVELOPER_MODE' in envs && envs['DEVELOPER_MODE'] == '1' ? ALLOWLIST : BLOCKLIST ``` --- ## File Access Rules # File Access Rules File Access rules enable Santa to control which processes can read and write files on macOS systems. This powerful feature allows administrators to monitor, log, and block file access attempts based on flexible policies. :::info Requirements File Access rules require macOS 13 or later. ::: ## Overview File access authorization provides fine-grained control over file system access by allowing you to: - Log access events for audit and compliance - Block unauthorized access to sensitive files - Define policies based on both files and processes File Access rules configure the policies that Santa uses to decide which files and processes to monitor and control access to. ## Rule Types File Access supports four distinct rule types, categorized by orientation: ### Data-Centric Rules These rules focus on protecting specific files or directories: #### Paths with Allowed Processes Specifies which processes are allowed to access particular files or directories. Only the listed processes can access the protected paths, while all others are denied. **Use case**: Protecting sensitive configuration files by allowing only specific system processes to access them. #### Paths with Denied Processes Blocks designated processes from accessing specific files or directories. All processes except those listed are allowed to access the paths. **Use case**: Preventing a particular application from accessing user documents or sensitive data. ### Process-Centric Rules These rules focus on controlling what a process can access: #### Processes with Allowed Paths Defines which paths a process is allowed to access. The process can only access the specified paths and is denied access to all others. **Use case**: Sandboxing an untrusted application to only access specific directories. #### Processes with Denied Paths Restricts a process from accessing specific paths. The process can access anything except the denied paths. **Use case**: Preventing an application from accessing system directories or other users' home folders. ## Rule Options ### Allow Read Access When checked, read access will be allowed. ### Block Violations When unchecked, this rule will be in 'audit-only' mode, where violations will trigger events to be sent to Workshop but the access will not be blocked. This is useful for testing policies before enforcement. ### Enable Silent Mode When checked, this rule will not trigger notification dialogs from Santa, silently blocking the access without informing the user. This can be useful for preventing background processes from accessing files without interrupting the user but care should be taken to not use this in cases where the user is expecting the access to work. ### Enable Silent TTY Mode When checked, this rule will not trigger notifications in the terminal, silently blocking access. ## Paths File Access supports flexible path matching, using either literals (with optional wildcards) or prefixes. Some rules to be aware of: - All paths are case-sensitive - Paths must reference resolved filesystem locations - Symbolic links are not supported - use the actual resolved path - Always use absolute paths, not relative paths ### Path Literals Specify exact file or directory paths: ``` /etc/sudoers /Users/admin/.ssh/id_rsa ``` You can also use standard wildcards for pattern matching: ``` /Users/*/Documents/* /Applications/*.app ``` Standard libc `glob(3)` patterns are supported (excluding extended patterns like `**`): ``` /etc/*.conf /var/log/app-[0-9]*.log ``` ### Path Prefixes Enable recursive directory monitoring: ``` /Users/admin/Documents/ ``` ## Processes Processes can be matched using multiple identification methods, for flexibility when writing rules. You should use signing identifiers (Signing ID, Team ID, CDHash) rather than file paths, whenever possible. File paths can easily be changed, while code signing identifiers provide stronger security guarantees. ### Binary Paths Match by full executable path: ``` /Applications/TextEdit.app/Contents/MacOS/TextEdit ``` ### Signing ID The code signing identifier assigned to the application, prefixed with the Apple developer team ID of the organization that signed it: ``` ABCDE12345:com.example.myapp ``` Use the special team ID `platform` for platform binaries that are part of the OS: ``` platform:com.apple.less ``` ### Team ID The Apple Developer Team ID: ``` ABCDE12345 ``` ### CDHash The CDHash of the signed binary: ``` a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0 ``` ### Leaf Certificate Hash The SHA-256 hash of the leaf certificate that was used to sign the binary: ``` 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef ``` ## Best Practices ### Start with Audit-Only Monitoring Before enforcing rules, enable audit-only mode to: - Understand normal access patterns - Identify legitimate processes that need access - Avoid accidentally blocking critical system operations ### Maintain Specific Path Patterns Use the most specific path patterns possible: - Avoid overly broad wildcards like `/*` - Target specific directories or file types - Use prefix matching judiciously for large directory trees ### Test Thoroughly Before deploying to production: - Test rules on non-critical systems first - Monitor logs for unexpected denials - Verify legitimate operations still work - Check for performance impact ## Related Documentation For more detailed information about file access authorization configuration and examples, visit the [Santa FAA Documentation](https://northpole.dev/features/faa/). --- ## Filter Expressions # Telemetry Filter Expressions Telemetry filter expressions are CEL expressions evaluated by Santa on the client before events are uploaded. They give you fine-grained control over which events are sent to your telemetry bucket — for example, to drop noisy event types entirely, exclude events from a specific binary, or redact sensitive values like tokens out of an event before it leaves the host. Filter expressions are configured per-tag in Sync Settings and apply to every event Santa produces. They are evaluated locally on the host, so events that are filtered out never reach Workshop or your cloud storage bucket. Filter expressions can also be set directly in the Santa configuration profile via the [`TelemetryFilterExpressions`](https://northpole.dev/configuration/keys/#TelemetryFilterExpressions) key — useful when you want to apply a baseline filter through MDM rather than through Workshop sync. For a full reference of the CEL environment Santa exposes — including all event types, fields, and built-in functions — see [Santa's telemetry documentation](https://northpole.dev/features/telemetry/). ## Configuration Telemetry filter expressions are managed on the **Telemetry** tab of the Sync Settings editor. Each tag may define any number of expressions; the effective list for a host is determined by the same tag-precedence rules used for all other sync settings. Hosts must be enrolled in a tag whose telemetry configuration has telemetry **enabled** for expressions to take effect. See the [Telemetry overview](/telemetry/) for how telemetry collection is set up. ## Expression Semantics Each filter expression is evaluated once per event. The expression's boolean result determines whether the event is kept. Multiple expressions are combined so that an event is kept only when **all** expressions agree to keep it — in other words, expressions act as an AND of independent filters. Expressions also have access to two Santa functions for scrubbing sensitive values out of an event in-place before it is uploaded: - `hash(value, regex)` — replaces the regex's captured group with a hash of the matched substring. The original value never leaves the host, but the hashed value can still be used to correlate the same secret across events. - `redact(value, regex)` — replaces the regex's captured group with a fixed redaction marker. Use this when you want to scrub the value entirely with no correlation across events. Both functions return `true`, so they compose naturally inside boolean expressions like `exists()` — the filter can match on a sensitive value and scrub it in a single pass. ## The `event` Variable The top-level variable available to a filter expression is `event`. It is a union of all Santa event types; exactly one sub-field is populated per event. The sub-fields use PascalCase and correspond to the event types described on the [Schema](/telemetry/schema) page: | Sub-field | Event type | | ------------------------------ | ---------------------- | | `event.Execution` | Process execution | | `event.Fork` | Process fork | | `event.Exit` | Process exit | | `event.Close` | File close | | `event.Rename` | File rename | | `event.Link` | Hard link creation | | `event.Unlink` | File deletion | | `event.Clone` | File clone | | `event.Exchangedata` | File data exchange | | `event.FileAccess` | File access policy hit | | `event.Authentication` | Authentication attempt | | `event.LoginLogout` | Console login/logout | | `event.LoginWindowSession` | GUI session events | | `event.OpenSSH` | SSH login/logout | | `event.Allowlist` | Allowlist additions | | `event.Bundle` | Bundle hash event | | `event.GatekeeperOverride` | Gatekeeper bypass | | `event.TCCModification` | TCC modification | | `event.XProtect` | XProtect detection | | `event.ScreenSharing` | Screen sharing event | | `event.Disk` | Disk mount/unmount | | `event.LaunchItem` | Launch item event | | `event.ProcSuspendResume` | Process suspend/resume | | `event.CodesigningInvalidated` | Codesigning invalidate | Use the `has()` macro to test which sub-field is populated. Once you've gated on the event type, you can navigate into nested fields using the same names that appear on the [Schema](/telemetry/schema) page. ```cel has(event.Execution) && event.Execution.Target.Executable.Path == '/usr/bin/yes' ``` ## CEL Basics Telemetry filter expressions use the same CEL language as [CEL rules](/rules/execution-rules#cel-rules) and CEL fallback rules. The following are commonly useful: - **Logical**: `&&`, `||`, `!` - **Comparison**: `==`, `!=`, `<`, `>`, `<=`, `>=` - **String functions**: `startsWith()`, `endsWith()`, `contains()`, `matches()`, `size()` - **List macros**: `exists()`, `all()`, `filter()`, `map()`, `size()` - **Optional field access**: `has()` See [celbyexample.com](https://celbyexample.com) for a comprehensive CEL reference. ## Examples ### Keep only execution events ```cel has(event.Execution) ``` :::note If you want to disable whole event types entirely, prefer the Santa [`Telemetry`](https://northpole.dev/configuration/keys/#Telemetry) configuration key — it stops Santa from producing those events at all, which is cheaper than producing each event and then dropping it with a filter expression. ::: ### Drop executions from a noisy path ```cel !(has(event.Execution) && event.Execution.Target.Executable.Path.startsWith('/opt/homebrew/')) ``` ### Redact a token-shaped environment variable ```cel has(event.Execution) && event.Execution.Envs.exists(e, e.startsWith("GITHUB_TOKEN") && hash(e, "GITHUB_TOKEN=(.*)")) ``` This expression matches execution events that carry a `GITHUB_TOKEN` environment variable and uses `hash()` with the regex `GITHUB_TOKEN=(.*)` to replace the captured value with a hash before the event is uploaded. The original token never leaves the host, but the hashed value can still be used to correlate the same token across events. ### Redact a token entirely ```cel has(event.Execution) && event.Execution.Envs.exists(e, e.startsWith("AWS_SECRET_ACCESS_KEY") && redact(e, "AWS_SECRET_ACCESS_KEY=(.*)")) ``` Identical in shape to the `hash()` example, but uses `redact()` to replace the captured value with a fixed marker — appropriate when you don't need to correlate the same secret across events. ### Drop a specific file-access policy from upload ```cel !(has(event.FileAccess) && event.FileAccess.PolicyName == 'noisy-policy') ``` ### Keep authentication events for failures only ```cel !has(event.Authentication) || !event.Authentication.Success ``` ## Validation and Rollout Workshop stores expressions as plain strings and delivers them to hosts on the next sync. Validation is performed by Santa when it parses the configuration — an invalid expression is logged on the host and ignored, so test changes on a small tag before rolling them out widely. A safe rollout pattern is: 1. Apply the new expression to a single host or test tag. 2. Confirm in your telemetry bucket that the expected events are kept, dropped, or redacted. 3. Promote the expression to a broader tag once you're satisfied. ## See Also - [Telemetry](/telemetry/) — how to enable telemetry collection - [Schema](/telemetry/schema) — the field names referenced by `event.*` - [CEL Execution Rules](/rules/execution-rules#cel-rules) — CEL used for policy decisions - [Santa telemetry documentation](https://northpole.dev/features/telemetry/) — authoritative reference for the CEL environment exposed by Santa --- ## Schema # Telemetry Schema This page documents the complete schema for all telemetry event types collected by Workshop from Santa agents. ## Base Fields | Field | Type | Description | | --------------- | ---- | ---------------------------------------------------- | | MachineID | text | The unique machine ID (host UUID) | | Hostname | text | The hostname of the machine at the time of the event | | BootSessionUUID | text | Unique identifier for the boot session | | EventTime | text | When the event occurred | | ProcessedTime | text | When Workshop processed the event | ## Common Nested Types The following types are used throughout the telemetry schema to represent shared data structures. ### ProcessID Unique identifier for a process during OS runtime. | Field | Type | Description | | ---------- | ------ | ------------------------------------------------ | | PID | number | Process ID | | PIDVersion | number | Process ID version for tracking across PID reuse | ### UserInfo User identification information. | Field | Type | Description | | ----- | ------ | ----------- | | UID | number | User ID | | Name | text | User name | ### GroupInfo Group identification information. | Field | Type | Description | | ----- | ------ | ----------- | | GID | number | Group ID | | Name | text | Group name | ### Hash Cryptographic hash information. | Field | Type | Description | | ----- | ---- | ----------------------------------------- | | Type | text | Hash algorithm (e.g., `HASH_ALGO_SHA256`) | | Hash | text | Hash value | ### Stat File metadata from stat(2) syscall. | Field | Type | Description | | ---------------- | ----------------------- | ----------------------------- | | Dev | number | Device ID | | Mode | number | File mode and permissions | | Nlink | number | Number of hard links | | Ino | number | Inode number | | User | [UserInfo](#userinfo) | File owner | | Group | [GroupInfo](#groupinfo) | File group | | Rdev | number | Device ID for special files | | AccessTime | timestamp | Last access time | | ModificationTime | timestamp | Last modification time | | ChangeTime | timestamp | Last status change time | | BirthTime | timestamp | Creation time | | Size | number | File size in bytes | | Blocks | number | Number of blocks allocated | | Blksize | number | Block size for filesystem I/O | | Flags | number | User defined flags | | Gen | number | File generation number | ### FileInfoLight Basic file information with path only. | Field | Type | Description | | --------- | ------- | ------------------------------ | | Path | text | File path | | Truncated | boolean | Whether the path was truncated | ### FileInfo Comprehensive file information. | Field | Type | Description | | --------- | ------------- | ------------------------------ | | Path | text | File path | | Truncated | boolean | Whether the path was truncated | | Stat | [Stat](#stat) | File metadata | | Hash | [Hash](#hash) | File content hash | ### CodeSignature Code signing information. | Field | Type | Description | | ----------------- | --------- | ----------------------------- | | CDHash | bytes | Code directory hash | | SigningID | text | Signing identifier | | TeamID | text | Team identifier | | SecureSigningTime | timestamp | Secure timestamp from signing | | SigningTime | timestamp | Signing timestamp | ### CertificateInfo Certificate information for signed code. | Field | Type | Description | | ---------- | ------------- | ----------------------- | | Hash | [Hash](#hash) | Certificate hash | | CommonName | text | Certificate common name | ### Entitlement Individual entitlement key-value pair. | Field | Type | Description | | ----- | ---- | ----------------- | | Key | text | Entitlement key | | Value | text | Entitlement value | ### EntitlementInfo Collection of process entitlements. | Field | Type | Description | | -------------------- | ------------------------------------ | ------------------------------------------ | | EntitlementsFiltered | boolean | Whether the entitlements list was filtered | | Entitlements | Array of [Entitlement](#entitlement) | List of entitlements | ### ProcessInfoLight Lightweight process information. | Field | Type | Description | | ----------------- | ------------------------------- | ---------------------------------------- | | ID | [ProcessID](#processid) | Process identifier | | ParentID | [ProcessID](#processid) | Parent process identifier | | OriginalParentPID | number | Original parent PID (before reparenting) | | GroupID | number | Process group ID | | SessionID | number | Session ID | | EffectiveUser | [UserInfo](#userinfo) | Effective user | | EffectiveGroup | [GroupInfo](#groupinfo) | Effective group | | RealUser | [UserInfo](#userinfo) | Real user | | RealGroup | [GroupInfo](#groupinfo) | Real group | | Executable | [FileInfoLight](#fileinfolight) | Executable file path | ### ProcessInfo Full process information. | Field | Type | Description | | ----------------- | ------------------------------- | ------------------------------------------- | | ID | [ProcessID](#processid) | Process identifier | | ParentID | [ProcessID](#processid) | Parent process identifier | | ResponsibleID | [ProcessID](#processid) | Responsible process identifier | | OriginalParentPID | number | Original parent PID (before reparenting) | | GroupID | number | Process group ID | | SessionID | number | Session ID | | EffectiveUser | [UserInfo](#userinfo) | Effective user | | EffectiveGroup | [GroupInfo](#groupinfo) | Effective group | | RealUser | [UserInfo](#userinfo) | Real user | | RealGroup | [GroupInfo](#groupinfo) | Real group | | IsPlatformBinary | boolean | Whether this is a platform binary | | IsESClient | boolean | Whether this is an Endpoint Security client | | CodeSignature | [CodeSignature](#codesignature) | Code signing information | | CSFlags | number | Code signing flags | | Executable | [FileInfo](#fileinfo) | Executable file information | | TTY | [FileInfoLight](#fileinfolight) | Associated TTY device | | StartTime | timestamp | Process start time | ## Process Events ### execution Process execution events. | Field | Type | Description | | ---------------- | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | Parent process | | Target | [ProcessInfo](#processinfo) | Executed process | | Script | [FileInfo](#fileinfo) | The script that was being executed, if applicable | | WorkingDirectory | [FileInfo](#fileinfo) | The working directory | | Args | text array | Command-line arguments | | Envs | text array | Environment variables | | FDs | FileDescriptor array | The open file descriptors at time of execution | | FDListTruncated | boolean | Whether the list in `FDs` is truncated | | Decision | text | The decision that was made by Santa, e.g. `DECISION_ALLOW` | | Reason | text | The reason that Santa made the decision it did, e.g. `REASON_CERT` | | Mode | text | Santa's client mode at the time of the event, e.g. `MODE_MONITOR` | | CertificateInfo | [CertificateInfo](#certificateinfo) | The common name and hash of the leaf certificate that signed this binary, if applicable | | EntitlementInfo | [EntitlementInfo](#entitlementinfo) | The entitlements attached to this binary | | Explain | text | Possible additional context related to this execution | | QuarantineURL | text | The URL the binary was downloaded from, if known | | OriginalPath | text | The original on-disk path of the target executable, applies when binaries are translocated (https://developer.apple.com/forums/thread/724969) | ### fork Process fork events. | Field | Type | Description | | ---------- | ------------------------------------- | --------------- | | Instigator | [ProcessInfoLight](#processinfolight) | Parent process | | Child | [ProcessInfoLight](#processinfolight) | Child processes | ### exit Process termination events. | Field | Type | Description | | ---------- | ------------------------------------- | ------------------------------------------------------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | Exiting process | | ExitCode | number | Exit code of the process (set when process exits normally) | | Signaled | number | Signal number that terminated the process (set when terminated by signal) | | Stopped | number | Signal number that stopped the process (set when stopped by signal) | ### proc_suspend_resume Process suspend and resume events. | Field | Type | Description | | ---------- | ------------------------------------- | ---------------------------------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | The process that initiated the suspend/resume action | | Target | [ProcessInfo](#processinfo) | The process being suspended or resumed | | Type | text | The type of action, e.g. `TYPE_SUSPEND` | ### codesigning_invalidated Code signature invalidation events. | Field | Type | Description | | ---------- | ------------------------------------- | ---------------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | Process with invalidated signature | ## File System Events ### close File close events. | Field | Type | Description | | ---------- | ------------------------------------- | ---------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | The process closing the file | | Target | [FileInfo](#fileinfo) | The file being closed | | Modified | boolean | Whether file was modified | ### file_access File access monitoring events. | Field | Type | Description | | -------------- | ------------------------------- | --------------------------------------------------------------------------------------------------------------- | | Instigator | [ProcessInfo](#processinfo) | The process accessing the file | | Target | [FileInfoLight](#fileinfolight) | The file being accessed | | PolicyName | text | The name of the file-access policy | | PolicyVersion | text | The version of the file-access policy | | AccessType | text | The type of event that attempted access, e.g. `ACCESS_TYPE_UNLINK` | | PolicyDecision | text | The decision that was made, e.g. `POLICY_DECISION_ALLOWED_AUDIT_ONLY` | | OperationID | text | Unique operation identifier, used to link a single operation when a single operation violates multiple policies | ### rename File rename/move events. | Field | Type | Description | | ------------- | ------------------------------------- | --------------------------------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | The process that is renaming the file | | Source | [FileInfo](#fileinfo) | The source file | | Target | text | The destination path | | TargetExisted | boolean | Whether or not the destination path already existed | ### link Hard link creation events. | Field | Type | Description | | ---------- | ------------------------------------- | --------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | The process making the link | | Source | [FileInfo](#fileinfo) | The source file | | Target | text | Link path | ### unlink File deletion events. | Field | Type | Description | | ---------- | ------------------------------------- | ------------------------------ | | Instigator | [ProcessInfoLight](#processinfolight) | The process unlinking the file | | Target | [FileInfo](#fileinfo) | The deleted file info | ### clone File clone (copy-on-write) events. | Field | Type | Description | | ---------- | ------------------------------------- | ---------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | Process performing the clone | | Source | [FileInfo](#fileinfo) | Source file | | Target | text | Clone destination | ### exchangedata Atomic data exchange between files events. | Field | Type | Description | | ---------- | ------------------------------------- | ------------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | Process performing the exchange | | File1 | [FileInfo](#fileinfo) | First file | | File2 | [FileInfo](#fileinfo) | Second file | ## Authentication & Session Events ### authentication Authentication attempts. This event type has subtypes with different fields depending on the authentication method. | Field | Type | Description | | ---------- | ------------- | ----------------------------------------- | | Success | boolean | Authentication result | | OD | OpenDirectory | OpenDirectory authentication subtype data | | TouchID | TouchID | Touch ID authentication subtype data | | Token | Token | Token authentication subtype data | | AutoUnlock | AutoUnlock | Auto unlock authentication subtype data | ### login_logout Console login/logout events. This event type has subtypes for login and logout. **Login subtype:** | Field | Type | Description | | -------------- | ------------------------------------- | ----------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | Process handling the login | | User | [UserInfo](#userinfo) | User logging in | | Success | boolean | Whether login was successful | | FailureMessage | text | Error message if login failed | **Logout subtype:** | Field | Type | Description | | ---------- | ------------------------------------- | --------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | Process handling the logout | | User | [UserInfo](#userinfo) | User logging out | ### login_window_session GUI session events. This event type has subtypes for different session actions. **Login subtype:** | Field | Type | Description | | ---------------- | ------------------------------------- | ---------------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | Process handling the session login | | User | [UserInfo](#userinfo) | User logging in | | GraphicalSession | GraphicalSession | Graphical session information | **Logout subtype:** | Field | Type | Description | | ---------------- | ------------------------------------- | ----------------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | Process handling the session logout | | User | [UserInfo](#userinfo) | User logging out | | GraphicalSession | GraphicalSession | Graphical session information | **Lock subtype:** | Field | Type | Description | | ---------------- | ------------------------------------- | ---------------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | Process handling the session lock | | User | [UserInfo](#userinfo) | User whose session is being locked | | GraphicalSession | GraphicalSession | Graphical session information | **Unlock subtype:** | Field | Type | Description | | ---------------- | ------------------------------------- | ------------------------------------ | | Instigator | [ProcessInfoLight](#processinfolight) | Process handling the session unlock | | User | [UserInfo](#userinfo) | User whose session is being unlocked | | GraphicalSession | GraphicalSession | Graphical session information | ### openssh SSH authentication events. This event type has subtypes for SSH login and logout. **Login subtype:** | Field | Type | Description | | ---------- | ------------------------------------- | ------------------------------------ | | Instigator | [ProcessInfoLight](#processinfolight) | SSH daemon process | | Result | text | Authentication result | | Source | SocketAddress | Source address of the SSH connection | | User | [UserInfo](#userinfo) | User attempting to log in | **Logout subtype:** | Field | Type | Description | | ---------- | ------------------------------------- | ------------------------------------ | | Instigator | [ProcessInfoLight](#processinfolight) | SSH daemon process | | Source | SocketAddress | Source address of the SSH connection | | User | [UserInfo](#userinfo) | User logging out | ## Security Events ### allowlist Binary allowlist addition events. | Field | Type | Description | | ---------- | ------------------------------------- | ---------------------------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | Process that added the binary to the allowlist | | Target | [FileInfo](#fileinfo) | Binary being added to the allowlist | ### bundle Bundle hash events. | Field | Type | Description | | ---------- | ------------- | ---------------------------------- | | FileHash | [Hash](#hash) | Hash of the individual file | | BundleHash | [Hash](#hash) | Hash of the entire bundle | | BundleName | text | Name of the bundle | | BundleID | text | Bundle identifier | | BundlePath | text | Path to the bundle | | Path | text | Path to the file within the bundle | ### gatekeeper_override Gatekeeper bypass events. | Field | Type | Description | | ------------- | ------------------------------------- | ----------------------------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | Process that bypassed Gatekeeper | | Target | [FileInfo](#fileinfo) | File that was allowed to run despite Gatekeeper | | CodeSignature | [CodeSignature](#codesignature) | Code signing information | ### tcc_modification TCC (Transparency, Consent, and Control) database modification events. | Field | Type | Description | | ------------------- | ------------------------------------- | ----------------------------------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | Process modifying TCC database | | Service | text | TCC service being modified (e.g., camera, microphone) | | Identity | text | Identity being granted/revoked access | | IdentityType | text | Type of identity (bundle ID, path, etc.) | | EventType | text | Type of modification event | | AuthorizationRight | text | Authorization right being modified | | AuthorizationReason | text | Reason for the authorization change | | TriggerProcess | [ProcessInfoLight](#processinfolight) | Process that triggered the modification | | TriggerID | [ProcessID](#processid) | Process ID of the trigger process | | ResponsibleProcess | [ProcessInfoLight](#processinfolight) | Process responsible for the modification | | ResponsibleID | [ProcessID](#processid) | Process ID of the responsible process | ### xprotect XProtect malware detection and remediation events. This event type has subtypes for detection and remediation. **Detected subtype:** | Field | Type | Description | | ------------------ | ------------------------------------- | ----------------------------------------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | XProtect process that detected the malware | | SignatureVersion | text | Version of the XProtect signature that detected the malware | | MalwareIdentifier | text | Identifier for the detected malware | | IncidentIdentifier | text | Unique identifier for this detection incident | | DetectedPath | text | Path where malware was detected | **Remediated subtype:** | Field | Type | Description | | ------------------- | ------------------------------------- | --------------------------------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | XProtect process that remediated the malware | | SignatureVersion | text | Version of the XProtect signature | | MalwareIdentifier | text | Identifier for the remediated malware | | IncidentIdentifier | text | Unique identifier for this remediation incident | | DetectedPath | text | Path where malware was originally detected | | ActionType | text | Type of remediation action taken | | Success | boolean | Whether remediation was successful | | ResultDescription | text | Description of the remediation result | | RemediatedPath | text | Path that was remediated | | RemediatedProcessID | [ProcessID](#processid) | Process ID of the remediated process, if applicable | ### screen_sharing Screen sharing connection events. This event type has subtypes for attach and detach. **Attach subtype:** | Field | Type | Description | | ------------------ | ------------------------------------- | ---------------------------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | Process handling the screen sharing connection | | Success | boolean | Whether the connection was successful | | Source | SocketAddress | Source address of the connection | | Viewer | text | Identifier of the viewer | | AuthenticationType | text | Type of authentication used | | AuthenticationUser | [UserInfo](#userinfo) | User that authenticated | | SessionUser | [UserInfo](#userinfo) | User whose session is being shared | | ExistingSession | boolean | Whether connecting to an existing session | | GraphicalSession | GraphicalSession | Graphical session information | **Detach subtype:** | Field | Type | Description | | ---------------- | ------------------------------------- | ---------------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | Process handling the disconnection | | Source | SocketAddress | Source address of the connection | | Viewer | text | Identifier of the viewer | | GraphicalSession | GraphicalSession | Graphical session information | ## System Events ### disk Disk mount/unmount events. | Field | Type | Description | | ---------- | --------- | ---------------------------------------------------------------- | | Action | text | Whether the disk appeared or disappeared, e.g. `ACTION_APPEARED` | | Mount | text | The path the disk is mounted at | | Volume | text | The name of the volume that was attached | | BSDName | text | The BSD name of the disk (e.g. `/dev/disk2s1`) | | FS | text | The filesystem on the disk | | Model | text | Device vendor and model information | | Serial | text | The serial number of the attached disk | | Bus | text | The bus path/protocol of the attached disk | | DMGPath | text | The path of the backing disk image, if the disk is a disk image | | Appearance | timestamp | The time the device appeared/disappeared | | MountFrom | text | The path mounted from | ### launch_item Launch item registration/removal events. | Field | Type | Description | | ----------------- | ------------------------------------- | ---------------------------------------------------------------------------------------- | | Instigator | [ProcessInfoLight](#processinfolight) | Process handling the launch item registration | | TriggerProcess | [ProcessInfoLight](#processinfolight) | The process that triggered registration (one of TriggerProcess or TriggerID will be set) | | TriggerID | [ProcessID](#processid) | Process ID that triggered registration (one of TriggerProcess or TriggerID will be set) | | RegistrantProcess | [ProcessInfoLight](#processinfolight) | The app that registered the launch item (may be set) | | RegistrantID | [ProcessID](#processid) | Process ID of the app that registered the launch item (may be set) | | Action | text | Whether a launch item was added or removed, e.g. `ACTION_ADD` | | ItemType | text | The kind of item that was registered, e.g. `ITEM_TYPE_AGENT`, `ITEM_TYPE_DAEMON` | | Legacy | boolean | Whether or not the launch item is a legacy plist | | Managed | boolean | Whether or not the launch item is managed by MDM | | ItemUser | [UserInfo](#userinfo) | User information related to the launch item | | ItemPath | text | The location of the launch item | | AppPath | text | The path of the app the launch item is attributed to | | ExecutablePath | text | If available, the associated executable path from the launch item plist | ## Network Events ### network_activity Network connection activity events. Each row represents a single network flow associated with a process. | Field | Type | Description | | ------------- | --------------------------- | ----------------------------------------------- | | Process | [ProcessInfo](#processinfo) | The process that initiated or received the flow | | ID | text | Unique identifier for this flow | | Hash | text | Hash of the flow | | RemoteAddress | text | Remote IP address | | RemotePort | number | Remote port number | | LocalAddress | text | Local IP address | | LocalPort | number | Local port number | | ProtocolRaw | number | IANA protocol number | | Protocol | text | Protocol name (e.g., `TCP`, `UDP`) | | SocketFamily | text | Socket family (e.g., `SOCKET_FAMILY_INET`) | | Direction | text | Flow direction (e.g., `DIRECTION_OUTBOUND`) | | BytesInbound | number | Number of bytes received | | BytesOutbound | number | Number of bytes sent | | StartTime | timestamp | When the flow started | | CloseTime | timestamp | When the flow closed | --- # Santa documentation ## Intro # Intro Santa is a high-performance open-source security agent for macOS that provides binary & file-access authorization and rich system event logging. --- ## Known limitations # Known limitations - Santa only blocks execution (execve and variants); it doesn’t protect against dynamic libraries loaded with dlopen, libraries on disk that have been replaced, or libraries loaded using `DYLD_INSERT_LIBRARIES`. - **Scripts:** Santa is written to ignore any execution that isn’t a binary. After weighing the administrative cost versus the benefit, we found it wasn’t worthwhile to manage the execution of scripts. Additionally, several applications make use of temporary scripts, and blocking these could cause problems. We’re happy to revisit this (or at least make it an option) if it would be useful to others. - **Removable Media (e.g. USB Mass Storage) Blocking:** Santa’s removable media blocking feature only stops incidental data exfiltration, it is not meant as a hard control. It operates at the mount level. It cannot block: - Directly writing to an unmounted, but attached device - Metrics reported by Santa are not _currently_ in a format that is friendly to open-source solutions --- ## Keys # Keys This page describes all of the available configuration options recognized by Santa. The configuration keys are broken down into sections to make it easier to find what you're looking for but in the configuration profile all the keys should be set together. Some keys (or available values for a key) will have a badge showing which Santa version they were added or deprecated in. Where a key has been deprecated, the description will list an alternative if one is available. A key with next to the type can be overridden by a sync server. ## General General options ## Sync Options related to syncing ## GUI Options controlling how the GUI functions ## FAA Options controlling file-access authorization ## Rules Options controlling binary authorization rules ## Telemetry Options controlling the output of telemetry data ## Removable Media (e.g. USB device) Options controlling the Removable Media (e.g. USB device) mount control feature ## Metrics Options controlling the export of agent metrics --- ## File-Access Authorization # File-Access Authorization File Access Authorization (FAA) policies are defined using a plist configuration file. The policy can be specified either in a [separate file](/configuration/keys#FileAccessPolicyPlist) or [in-line](/configuration/keys#FileAccessPolicy) with the rest of the Santa configuration. If the policy is specified in a separate file, Santa will periodically re-read this file. By default this will occur every 10 minutes but the interval can be [overridden](/configuration/keys#FileAccessPolicyUpdateIntervalSec). ## Policy Structure The policy file has a hierarchical structure with root-level configuration and individual watch rules. ### Root Level Keys - `Version` (required): Policy version identifier that will be reported in events - `EventDetailURL` (optional): URL displayed when users receive block notifications. Supports [variable substitution](#eventdetailurl-placeholders) (e.g., `%hostname%`, `%rule_name%`, `%file_identifier%`) - `EventDetailText` (optional): Button label text for the notification dialog, maximum 48 characters. Defaults to 'Open'. - `WatchItems` (optional): Dictionary containing the individual monitoring rules :::tip If you want a default URL and button text for all file access events without configuring them in every FAA policy, you can set the global [FileAccessEventDetailURL](/configuration/keys#FileAccessEventDetailURL) and [FileAccessEventDetailText](/configuration/keys#FileAccessEventDetailText) configuration keys. Per-policy `EventDetailURL` and `EventDetailText` values (and per-rule overrides) will take precedence over these global defaults. ::: ### Watch Item Structure Each entry in the `WatchItems` dictionary represents a single rule. The key for each entry is the rule name, which will be used in logs and in the block notification UI. :::info Rule names (the `WatchItems` dictionary keys) must be valid C identifiers, matching the regular expression `^[A-Za-z_][A-Za-z0-9_]*$`. Names must start with a letter or underscore and contain only letters, digits, and underscores. For example, `ChromeCookies` and `my_rule_1` are valid, but `my-rule` and `My Rule` are not. Invalid names will be rejected and an error will be logged. ::: Each rule contains three main components: - `Paths`: Array of path patterns to monitor - `Processes`: List of allowed/denied processes with specific identifiers - `Options`: Settings for rule behavior ## Basic Example ```xml Version v0.1 EventDetailURL https://my-server/faa/%hostname%/%rule_name%/%file_identifier% WatchItems UserFoo Paths Path /Users/*/tmp/foo IsPrefix Options AllowReadAccess AuditOnly RuleType PathsWithAllowedProcesses Processes TeamID EQHXZ8M8AV SigningID com.google.Chrome.helper ``` ## Path Configuration Paths can be specified using exact matches or wildcard patterns: - Exact paths: `/etc/sudoers` - Wildcards: `/Users/*/Documents/*` Each path entry can include: - `Path` (required): The path pattern to monitor - `IsPrefix` (optional): Boolean indicating whether the path represents prefix matching. When `true`, the rule will match files nested inside directories. When `false` or omitted, wildcards only match files/directories at that level without recursing. :::important If a configuration contains multiple rules with overlapping configured paths, only one rule will be applied. Which rule will be applied is undefined, so take care not to define rules with duplicate paths. ::: ### Path Globs Path globs represent a point-in-time snapshot. Globs are expanded when a configuration is applied and periodically re-evaluated based on the [FileAccessPolicyUpdateIntervalSec](/configuration/keys#FileAccessPolicyUpdateIntervalSec) setting. When multiple path globs or prefixes match an operation, the rule with the "most specific" or longest match is applied. Glob pattern support is provided by the libc [`glob(3)`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/glob.3.html) function. Extended glob patterns, such as globstar (`**`), are not supported. ### Path Resolution All configured paths are case-sensitive and must match the case as stored on the filesystem. Due to system limitations, Santa cannot reliably monitor hard-linked resources. To help mitigate bypasses, Santa will not allow the creation of hard links for monitored paths. If hard links previously existed for monitored paths, Santa cannot guarantee that access via these other links will be monitored. Configured path globs must refer to resolved paths only. Monitoring access on symbolic links is not supported. This is important as some common macOS paths are symbolic links (e.g., `/tmp` and `/var` are both symlinks into `/private`). ## Process Matching Processes can be matched using several identifiers: - **Signing ID**: Specified with the `SigningID` key (e.g., `EQHXZ8M8AV:com.google.Chrome.helper`) - **Team ID**: Specified with the `TeamID` key (e.g., `ZMCG7MLDV9`) - **Platform Binary**: Specified with the `PlatformBinary` boolean key - **CDHash**: Specified with the `CDHash` key (e.g., `397d55ebec87943ea3c3fe6b4d4f47edc490d25e`) - **Leaf Certificate Hash**: Specified with the `CertificateSha256` key - **Binary Path**: Specified with the `BinaryPath` key (e.g., `/Applications/Safari.app/Contents/MacOS/Safari`) :::tip Signing IDs must be scoped to a specific TeamID. You can use the same format as binary authorization rules where the SigningID is prefixed with the TeamID (e.g. `TeamID:SigningID`. For platform binaries, you can use the hard coded string `platform` as the TeamID (e.g. `platform:com.apple.yes`). ::: :::warning Specifying binaries by full path using `BinaryPath` is not very secure, as binaries can easily be moved. This should only be used as a last resort. Additionally, the `BinaryPath` key does not support glob patterns (`*`). ::: ## Rule Options The `Options` dictionary within each rule supports the following keys: - `RuleType` (required): Defines whether the rule is data-centric or process-centric: - `PathsWithAllowedProcesses`: Data-centric, only listed processes can access the paths - `PathsWithDeniedProcesses`: Data-centric, listed processes cannot access the paths - `ProcessesWithAllowedPaths`: Process-centric, listed processes can only access specified paths - `ProcessesWithDeniedPaths`: Process-centric, listed processes cannot access specified paths - `AllowReadAccess` (optional): Boolean controlling whether read access is allowed. When `false`, both read and write access are monitored/blocked. When `true`, only write access is monitored/blocked. Defaults to `true` if not specified. - `AuditOnly` (optional): Boolean. When `true`, violations are logged but not blocked. Defaults to `true`. - `EventDetailURL` (optional): Rule-specific URL that overrides the top-level EventDetailURL. - `EventDetailText` (optional): Custom button label text for this specific rule, overriding the root-level setting. - `BlockMessage` (optional): Custom message to be shown in the dialog presented to users upon a violation. Defaults to a reasonable, generic message that the action was blocked. - `EnableSilentMode` (optional): Boolean. When `true`, violations are logged but no notification is shown to the user. Defaults to `false`. - `EnableSilentTTYMode` (optional): Boolean. When `true`, violations are logged, but no notification is sent to the controlling TTY. Defaults to `false`. ## Rule Type Selection Choose your rule type based on what you're protecting: | Goal | Rule Type | | ----------------------------------------------------- | --------------------------------------------------------- | | Protect specific files/paths from unauthorized access | `PathsWithAllowedProcesses` or `PathsWithDeniedProcesses` | | Restrict what a specific process can access | `ProcessesWithAllowedPaths` or `ProcessesWithDeniedPaths` | **Data-centric example**: Protect browser cookies from theft by limiting access to the cookie files to only the browser processes. **Process-centric example**: Prevent AirDrop processes from reading files in folders containing sensitive corporate data. ## EventDetailURL placeholders When an FAA rule blocks access to a file, the user will be presented with a block notification dialog. On this dialog a button can be displayed which will take the user to a page with more information about that event. For the button to appear you must populate the `EventDetailURL` field, either at the top-level of the configuration or in an individual rule. This URL can contain placeholders, which will be populated at runtime; the supported placeholders are: | Placeholder | Description | | ------------------- | ------------------------------------------------------------------------------------------------------------------------- | | `%rule_version%` | Version of the rule that was violated | | `%rule_name%` | Name of the rule that was violated | | `%file_identifier%` | SHA-256 of the binary that was being executed | | `%accessed_path%` | The path that was being accessed | | `%username%` | The executing user | | `%team_id%` | The team ID that signed this binary, if any | | `%signing_id%` | The signing ID of this binary, if any | | `%cdhash%` | The binary's CDHash, if any | | `%machine_id%` | The ID of the machine, usually the hardware UUID unless [overridden](https://northpole.dev/configuration/keys/#MachineID) | | `%serial%` | The serial number of the machine | | `%uuid%` | The hardware UUID of the machine | | `%hostname%` | The system's full hostname | ## More Information For complete example policies and use-cases, see the [File-Access Authorization feature documentation](/features/faa) and the [FAA cookbook](/cookbook/faa). --- ## Config Generator # Config Generator :::warning This generator is still under active development and there are known rough edges with some of the more complex configuration keys as well as more features that will be coming soon. Please give it a try! ::: Use this form to generate a valid Santa configuration, ready to put inside a configuration profile and deploy to your machines. The generator will ensure that the configuration is valid and help storing default values. :::info The generation is all done inside your browser; the data you input never leaves your machine. ::: ## General --- ## Sync --- ## GUI --- ## FAA --- ## Rules --- ## Telemetry --- ## Removable Media (e.g. USB mass storage device) --- ## Metrics --- ## Generate Click the button to generate and download the generated configuration file. --- ## Common Expression Language (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](/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. ```clike target.signing_time >= timestamp('2025-05-31T00:00:00Z') ``` = timestamp('2025-05-31T00:00:00Z')`} context={` target: signing_time: "2025-06-01T00:00:00Z" args: - "--version" envs: HOME: "/Users/admin" euid: 501 cwd: "/Applications" `} /> ## Prevent users from disabling gatekeeper Create a signing ID rule for `platform:com.apple.spctl` and attach the following CEL program ```clike [ '--global-disable', '--master-disable', '--disable', '--add', '--remove' ].exists(flag, flag in args) ? BLOCKLIST : ALLOWLIST ``` ## Prevent Timestomping of LaunchAgents and LaunchDaemons 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) ```clike 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 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 ```clike ( 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 ```