### Setup Jamf Protect Event Analysis Environment Source: https://github.com/jamf/jamfprotect/blob/main/helper_tools/jamf_protect_event_analysis/README.md Commands to clone the repository, create a Python virtual environment, and install necessary dependencies for the analysis tool. ```bash git clone https://github.com/jamf/jamfprotect.git cd jamfprotect/helper_tools/jamf_protect_event_analysis python3 -m venv ~/Documents/py3-jp source ~/Documents/py3-jp/bin/activate pip3 install -r requirements.txt ``` -------------------------------- ### Build Google Cloud Aftermath PKG Source: https://github.com/jamf/jamfprotect/blob/main/soar_playbooks/aftermath_collection/google_cloud_storage/README.md This Makefile snippet demonstrates how to build a package for Google Cloud integration with Aftermath. It requires the Google Cloud SDK to be installed and configured. ```makefile sudo make pkg ``` ```makefile sudo make clean ``` -------------------------------- ### Making GraphQL API Calls Source: https://context7.com/jamf/jamfprotect/llms.txt This section provides a utility function to make GraphQL API calls to the Jamf Protect API. It demonstrates how to send queries and handle paginated responses, using the 'listComputers' query as an example. ```APIDOC ## Making GraphQL API Calls ### Description Sends a GraphQL query to the Jamf Protect API and handles the response. Includes an example for listing computers with pagination. ### Method POST ### Endpoint `https://{your-tenant}.protect.jamfcloud.com/graphql` ### Parameters #### Request Body - **query** (string) - Required - The GraphQL query string. - **variables** (object) - Optional - Variables to be used in the GraphQL query. ### Headers - **Authorization** (string) - Required - The Bearer access token obtained from the authentication endpoint. ### Request Example ```python import requests def make_api_call(protect_instance, access_token, query, variables=None): """Sends a GraphQL query to the Jamf Protect API and returns the response.""" if variables is None: variables = {} api_url = f"https://{protect_instance}.protect.jamfcloud.com/graphql" payload = {"query": query, "variables": variables} headers = {"Authorization": access_token} resp = requests.post(api_url, json=payload, headers=headers) resp.raise_for_status() return resp.json() # Example: List computers with pagination LIST_COMPUTERS_QUERY = """ query listComputers($page_size: Int, $next: String) { listComputers(input: { pageSize: $page_size, next: $next }) { items { hostName serial uuid insightsUpdated insightsStatsFail insightsStatsPass } pageInfo { next } } } """ # Retrieve all computers with pagination # computers = [] # next_token = None # while True: # vars = {"page_size": 100, "next": next_token} # resp = make_api_call(PROTECT_INSTANCE, access_token, LIST_COMPUTERS_QUERY, vars) # next_token = resp["data"]["listComputers"]["pageInfo"]["next"] # computers.extend(resp["data"]["listComputers"]["items"]) # if next_token is None: # break # print(f"Found {len(computers)} computers") ``` ### Response #### Success Response (200) - **data** (object) - The response data from the GraphQL query. #### Response Example ```json { "data": { "listComputers": { "items": [ { "hostName": "MacBook-Pro.local", "serial": "C02XXXXXX123", "uuid": "a1b2c3d4-e5f6-7890-1234-567890abcdef", "insightsUpdated": "2023-10-27T10:00:00Z", "insightsStatsFail": 0, "insightsStatsPass": 15 } ], "pageInfo": { "next": "some_next_token_or_null" } } } } ``` ``` -------------------------------- ### Get Jamf Protect Binary Version using Jamf Pro Extension Attribute Source: https://context7.com/jamf/jamfprotect/llms.txt This shell script serves as a Jamf Pro Extension Attribute to retrieve the version of the Jamf Protect binary. It checks for the binary's existence at '/usr/local/bin/protectctl', extracts the version from its plist information, and returns the version string or an error message if the binary is not found. ```bash #!/bin/sh # Extension Attribute: Jamf Protect Binary Version # Data Type: String # Input Type: Script jamfProtectBinaryLocation="/usr/local/bin/protectctl" if [ -f "$jamfProtectBinaryLocation" ]; then plist=$($jamfProtectBinaryLocation info --plist) jamfProtectVersion=$(/usr/libexec/PlistBuddy -c "Print Version" /dev/stdin <<<"$plist") else jamfProtectVersion="Protect binary not found" fi echo "$jamfProtectVersion" ``` -------------------------------- ### Test Unified Log Filter Predicates Locally with macOS log command Source: https://context7.com/jamf/jamfprotect/llms.txt This bash script demonstrates how to test unified log filter predicates locally on macOS using the 'log stream' command. It shows an example of monitoring failed sudo password attempts by streaming logs with a specific predicate and then triggering the event in a separate terminal. ```bash # Test a unified log filter predicate locally # Example: Monitor failed sudo password attempts # Start streaming logs with the predicate log stream --predicate 'process == "sudo" AND eventMessage CONTAINS[c] "TTY" AND eventMessage CONTAINS[c] "3 incorrect password attempts"' # In another terminal, trigger the event: sudo whoami # Enter incorrect password 3 times # Expected output in first terminal: # 2024-01-15 10:30:45.123456+0000 0xXXXXc Default 0x0 XXXXX 0 sudo: username : 3 incorrect password attempts ; TTY=ttys003 ; PWD=/Users/username ; USER=root ; COMMAND=/usr/bin/whoami ``` -------------------------------- ### Configure Google Cloud CLI for gsutil Source: https://github.com/jamf/jamfprotect/blob/main/soar_playbooks/aftermath_collection/google_cloud_storage/README.md This command configures the Google Cloud SDK's gsutil tool for authentication. It prompts the user for account information and creates necessary configuration files. ```bash /usr/local/google-cloud-sdk/bin/gsutil config -a ``` -------------------------------- ### Export Alerts by Severity in Python Source: https://context7.com/jamf/jamfprotect/llms.txt Demonstrates how to paginate through all alerts within a specific severity range and save the results to a JSON file. It handles API pagination using a next_token loop. ```python results = [] next_token = None while True: vars = { "min_severity": MIN_SEVERITY, "max_severity": MAX_SEVERITY, "page_size": 100, "next": next_token, } resp = make_api_call(PROTECT_INSTANCE, access_token, LIST_ALERTS_QUERY, vars) next_token = resp["data"]["listAlerts"]["pageInfo"]["next"] results.extend(resp["data"]["listAlerts"]["items"]) if next_token is None: break print(f"Found {len(results)} alerts matching filter.") with open(JSON_OUTPUT_FILE, "w") as output: json.dump(results, output, sort_keys=True, indent=4) ``` -------------------------------- ### AWS CLI Configuration and Package Build Commands Source: https://github.com/jamf/jamfprotect/blob/main/soar_playbooks/aftermath_collection/aws_s3/README.md Terminal commands to configure the AWS CLI profile for Aftermath and build the deployment package using the provided Makefile. ```bash aws configure --profile aftermath aws configure list --profile aftermath sudo make pkg sudo make clean ``` -------------------------------- ### Run Jamf Protect Event Analysis Source: https://github.com/jamf/jamfprotect/blob/main/helper_tools/jamf_protect_event_analysis/README.md Executes the event analysis tool to monitor process activity for 60 seconds and generate a summary report. This helps identify top-counted processes for potential exclusion rules. ```bash source ~/Documents/py3-jp/bin/activate ./jp_event_analysis.py -m Process -s ``` -------------------------------- ### Implement Endpoint Network Isolation SOAR Playbook Source: https://context7.com/jamf/jamfprotect/llms.txt This bash script implements a SOAR playbook for endpoint network isolation using macOS Packet Filter (pfctl). It configures pf rules to block all traffic except for essential Apple and Jamf Pro communication, and sets up a LaunchDaemon to ensure the isolation persists across reboots. The script dynamically retrieves the Jamf Pro instance URL. ```bash #!/bin/bash # Endpoint Network Isolation - Enforce # Isolates endpoint using pfctl, maintaining Jamf Pro and Apple APNs connectivity # Configuration apnsIPRange="17.0.0.0/8" # Apple Push Notification Service fileName="com.acmesoft.isolate" # Get Jamf Pro URL from preferences JamfProInstance=$(/usr/bin/defaults read /Library/Preferences/com.jamfsoftware.jamf.plist jss_url | sed -e 's/^ *.*:\/\///g' -e 's/.$//') # Create PF anchor configuration /usr/bin/tee /etc/pf.anchors/"$fileName".pf.conf < Label ${fileName}.pf.plist Program /sbin/pfctl ProgramArguments /sbin/pfctl -e -f /etc/pf.anchors/${fileName}.pf.conf RunAtLoad EOF # Enable isolation /sbin/pfctl -d /bin/launchctl load -w /Library/LaunchDaemons/"$fileName".pf.plist echo "Endpoint network isolation enabled" ``` -------------------------------- ### Capture Unified Logs for Analysis Source: https://github.com/jamf/jamfprotect/blob/main/helper_tools/jamf_protect_event_analysis/README.md Captures system logs in JSON format for a specific Jamf Protect monitor category, which can then be passed to the analysis tool using the -i argument. ```bash log stream --info --debug --style json --timeout --predicate 'subsystem BEGINSWITH "com.jamf.protect" AND category == ""' > log_file.json ``` -------------------------------- ### Authenticate Jamf Protect GraphQL API with Python Source: https://context7.com/jamf/jamfprotect/llms.txt This Python script demonstrates how to obtain an access token for authenticating requests to the Jamf Protect API using client credentials. It requires the Jamf Protect instance URL, client ID, and API password. The function returns a reusable access token. ```python import requests PROTECT_INSTANCE = "your-tenant" # From https://your-tenant.protect.jamfcloud.com CLIENT_ID = "your-client-id" PASSWORD = "your-api-password" def get_access_token(protect_instance, client_id, password): """Gets a reusable access token to authenticate requests to the Jamf Protect API""" token_url = f"https://{protect_instance}.protect.jamfcloud.com/token" payload = { "client_id": client_id, "password": password, } resp = requests.post(token_url, json=payload) resp.raise_for_status() resp_data = resp.json() print(f"Access token granted, valid for {int(resp_data['expires_in'] // 60)} minutes.") return resp_data["access_token"] # Get token access_token = get_access_token(PROTECT_INSTANCE, CLIENT_ID, PASSWORD) # Output: Access token granted, valid for 60 minutes. ``` -------------------------------- ### Run Aftermath Scan and Collect Data Source: https://github.com/jamf/jamfprotect/blob/main/soar_playbooks/aftermath_collection/google_cloud_storage/README.md This policy triggers an Aftermath scan and then initiates a data collection event. It's designed to be run on endpoints managed by Jamf Pro. ```bash /usr/local/bin/aftermath --pretty; /usr/local/bin/jamf policy -event am_collect ``` -------------------------------- ### Detect Keychain Dump Attempts via Process Events Source: https://context7.com/jamf/jamfprotect/llms.txt This analytic monitors process creation events to identify attempts to dump the macOS keychain using the security binary. It filters for specific signing information and command-line arguments. ```yaml name: KeychainDumped uuid: 6fe79b15-aa2f-4bd5-a6c9-9192878f17b4 inputType: GPProcessEvent filter: > $event.type == 1 AND $event.process.signingInfo.appid == "com.apple.security" AND $event.process.args.@count > 0 AND (ANY $event.process.args == "dump-keychain") actions: - name: Log ``` -------------------------------- ### Query Audit Logs by Date Range in Python Source: https://context7.com/jamf/jamfprotect/llms.txt Retrieves administrative activity logs within a specified date range using the listAuditLogsByDate query. It implements pagination to ensure all logs are captured. ```python import datetime as dt now = dt.datetime.now(dt.timezone.utc) start_date = (now - dt.timedelta(days=7)).strftime("%Y-%m-%dT%H:%M") + ":00.000Z" end_date = now.strftime("%Y-%m-%dT%H:%M") + ":00.000Z" variables = { "input": { "condition": { "dateRange": { "startDate": start_date, "endDate": end_date, } } } } items = [] next_token = "" while next_token is not None: variables["input"]["next"] = next_token resp = make_api_call(PROTECT_INSTANCE, access_token, LIST_AUDIT_LOGS_BY_DATE, variables) items.extend(resp["data"]["listAuditLogsByDate"]["items"]) next_token = resp["data"]["listAuditLogsByDate"]["pageInfo"]["next"] ``` -------------------------------- ### Export Non-Compliant Insights in Bash Source: https://context7.com/jamf/jamfprotect/llms.txt Uses curl and jq to fetch non-compliant computer insights from Jamf Protect and exports the failing entries into a CSV file. It handles authentication, GraphQL querying, and JSON data transformation. ```bash #!/bin/bash # ... (authentication and query setup) while true; do request_json=$(jq -n \ --arg q "$LIST_COMPUTERS_QUERY" \ --argjson next_token $next_token \ '{"query": $q, "variables": {"next": $next_token}}' ) list_computers_resp=$(curl --silent --request POST --header "Authorization: ${access_token}" --url "https://${PROTECT_INSTANCE}.protect.jamfcloud.com/graphql" --data "$request_json") jq -r '.data.listComputers.items[] | {hostName: .hostName, scorecard_entry: .scorecard[]} | select(.scorecard_entry.pass == false) | select(.scorecard_entry.enabled == true) | [.hostName, .scorecard_entry.label, .scorecard_entry.pass] | @csv' <<< "$list_computers_resp" >> "$OUTPUT_CSV_FILE" next_token=$(jq '.data.listComputers.pageInfo.next' <<< "$list_computers_resp") [[ $next_token == "null" ]] && break done ``` -------------------------------- ### Make GraphQL API Calls to Jamf Protect with Python Source: https://context7.com/jamf/jamfprotect/llms.txt This Python function sends GraphQL queries to the Jamf Protect API using a provided access token. It supports variables for dynamic queries and handles pagination for large datasets. The function returns the JSON response from the API. ```python import requests def make_api_call(protect_instance, access_token, query, variables=None): """Sends a GraphQL query to the Jamf Protect API and returns the response.""" if variables is None: variables = {} api_url = f"https://{protect_instance}.protect.jamfcloud.com/graphql" payload = {"query": query, "variables": variables} headers = {"Authorization": access_token} resp = requests.post(api_url, json=payload, headers=headers) resp.raise_for_status() return resp.json() # Example: List computers with pagination LIST_COMPUTERS_QUERY = """ query listComputers($page_size: Int, $next: String) { listComputers(input: { pageSize: $page_size, next: $next }) { items { hostName serial uuid insightsUpdated insightsStatsFail insightsStatsPass } pageInfo { next } } } """ # Retrieve all computers with pagination computers = [] next_token = None while True: vars = {"page_size": 100, "next": next_token} resp = make_api_call(PROTECT_INSTANCE, access_token, LIST_COMPUTERS_QUERY, vars) next_token = resp["data"]["listComputers"]["pageInfo"]["next"] computers.extend(resp["data"]["listComputers"]["items"]) if next_token is None: break print(f"Found {len(computers)} computers") ``` -------------------------------- ### Jamf Protect GraphQL API Authentication Source: https://context7.com/jamf/jamfprotect/llms.txt This section details how to obtain an access token for authenticating with the Jamf Protect GraphQL API. It requires your Jamf Protect instance URL, client ID, and API password. ```APIDOC ## Jamf Protect GraphQL API Authentication ### Description Authenticates with the Jamf Protect API to obtain a reusable access token. ### Method POST ### Endpoint `https://{your-tenant}.protect.jamfcloud.com/token` ### Parameters #### Request Body - **client_id** (string) - Required - Your Jamf Protect API client ID. - **password** (string) - Required - Your Jamf Protect API password. ### Request Example ```python import requests PROTECT_INSTANCE = "your-tenant" # From https://your-tenant.protect.jamfcloud.com CLIENT_ID = "your-client-id" PASSWORD = "your-api-password" def get_access_token(protect_instance, client_id, password): """Gets a reusable access token to authenticate requests to the Jamf Protect API""" token_url = f"https://{protect_instance}.protect.jamfcloud.com/token" payload = { "client_id": client_id, "password": password, } resp = requests.post(token_url, json=payload) resp.raise_for_status() resp_data = resp.json() print(f"Access token granted, valid for {int(resp_data['expires_in'] // 60)} minutes.") return resp_data["access_token"] # Get token access_token = get_access_token(PROTECT_INSTANCE, CLIENT_ID, PASSWORD) ``` ### Response #### Success Response (200) - **access_token** (string) - The obtained access token. - **expires_in** (integer) - The token's validity period in seconds. #### Response Example ```json { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expires_in": 3600, "token_type": "Bearer" } ``` ``` -------------------------------- ### JSON Alert Schema - GPProcessEvent Source: https://context7.com/jamf/jamfprotect/llms.txt This schema defines the structure of process events captured by Jamf Protect, including detailed information about the process tree, signing status, and MITRE ATT&CK categorization for threat intelligence. ```APIDOC ## JSON Alert Schema - GPProcessEvent Process events include full process tree, signing information, and MITRE ATT&CK categorization for threat intelligence correlation. ### Request Body ```json { "host": { "os": "Version 14.0 (Build 23A344)", "ips": ["192.168.11.226"], "serial": "Z2C23RW4DY", "hostname": "VMAC-2C23RW4DY", "protectVersion": "5.1.0.4", "provisioningUDID": "0000FE00-8406CE28ECFC4DAB" }, "match": { "tags": ["MITREattack", "DefenseEvasion", "T1548.004", "PrivilegeEscalation"], "uuid": "7232d4a4-2289-49ba-a218-215ef3d62ec4", "event": { "pid": 3136, "type": 1, "uuid": "e19385fc-6077-4d00-ad56-b89eec15e730", "timestamp": 1698841238.851668 }, "facts": [ { "name": "User Elevated Action", "human": "Application used deprecated elevation API", "severity": 0, "actions": [{"name": "Report"}] } ], "severity": 0 }, "related": { "users": [ {"uid": 0, "name": "root"}, {"uid": 501, "name": "local-admin"} ], "processes": [ { "pid": 3136, "name": "security_authtrampoline", "path": "/usr/libexec/security_authtrampoline", "args": ["/usr/libexec/security_authtrampoline", "/Library/Application Support/JAMF/Remote Assist/Wipe", "auth 16"], "signingInfo": { "appid": "com.apple.security_authtrampoline", "status": 0, "signerType": 0, "authorities": ["Software Signing", "Apple Code Signing Certification Authority", "Apple Root CA"] } } ] }, "eventType": "GPProcessEvent" } ``` ``` -------------------------------- ### Detect Hosts File Modifications via File System Events Source: https://context7.com/jamf/jamfprotect/llms.txt This analytic monitors file system events to detect unauthorized modifications to the /etc/hosts file. It uses case-insensitive path matching to ensure detection. ```yaml name: HostFileModification uuid: 7eea8332-eeb4-416f-92d8-cccef960f678 inputType: GPFSEvent filter: > $event.isModified == 1 AND $event.path ==[cd] "/private/etc/hosts" actions: - name: Log ``` -------------------------------- ### Export Jamf Protect Alert Data with Severity Filtering in Python Source: https://context7.com/jamf/jamfprotect/llms.txt This Python script retrieves security alerts from Jamf Protect, filtering by severity levels (e.g., Low to High). It outputs the results to a JSON file, including the alert JSON payload, severity, hostname, and creation timestamp. It utilizes the `listAlerts` GraphQL query. ```python import json from datetime import datetime MIN_SEVERITY = "Low" MAX_SEVERITY = "High" JSON_OUTPUT_FILE = f"Jamf_Protect_Alerts_{datetime.utcnow().strftime('%Y-%m-%d')}.json" LIST_ALERTS_QUERY = """ query listAlerts( $min_severity: SEVERITY $max_severity: SEVERITY $page_size: Int $next: String ) { listAlerts( input: { filter: { severity: { greaterThanOrEqual: $min_severity } and: { severity: { lessThanOrEqual: $max_severity } } } pageSize: $page_size next: $next } ) { items { json severity computer { hostName } created } pageInfo { next } } } """ # This script would typically call make_api_call and then process/save the results. ``` -------------------------------- ### Monitor Lock Screen Unlock Failures Source: https://context7.com/jamf/jamfprotect/llms.txt A unified log filter that tracks failed unlock attempts by monitoring the loginwindow process for incorrect password indicators. ```yaml name: "Lock Screen Unlock Failure" predicate: > processImagePath BEGINSWITH "/System/Library/CoreServices" AND process == "loginwindow" AND eventMessage CONTAINS[c] "INCORRECT" ``` -------------------------------- ### Configure Application Firewall Logging Unified Log Filter Source: https://context7.com/jamf/jamfprotect/llms.txt This YAML configuration defines a unified log filter for streaming Application Firewall logs from macOS. It targets logs from the 'com.apple.alf' subsystem and is enabled by default. Note that private data is redacted unless a specific configuration profile is applied. ```yaml # unified_log_filters/application_firewall_logging.yaml name: "Application Firewall Logging" description: "Reports on logging from the Application Firewall in macOS via the com.apple.alf subsystem." predicate: 'subsystem == "com.apple.alf"' tags: - visibility enabled: true # Note: Private data (IP addresses, ports) is redacted by default. # Enable private data logging with a configuration profile from: # https://github.com/usnistgov/macos_security/blob/monterey/includes/com.apple.alf.private_data.mobileconfig ``` -------------------------------- ### Jamf Protect GPProcessEvent JSON Schema Source: https://context7.com/jamf/jamfprotect/llms.txt This JSON schema defines the structure for GPProcessEvent alerts from Jamf Protect. It includes host details, match information with MITRE ATT&CK tags, event specifics, related user and process data, and signing information. This schema is essential for integrating Jamf Protect alerts with SIEM platforms and threat intelligence systems. ```json { "host": { "os": "Version 14.0 (Build 23A344)", "ips": ["192.168.11.226"], "serial": "Z2C23RW4DY", "hostname": "VMAC-2C23RW4DY", "protectVersion": "5.1.0.4", "provisioningUDID": "0000FE00-8406CE28ECFC4DAB" }, "match": { "tags": ["MITREattack", "DefenseEvasion", "T1548.004", "PrivilegeEscalation"], "uuid": "7232d4a4-2289-49ba-a218-215ef3d62ec4", "event": { "pid": 3136, "type": 1, "uuid": "e19385fc-6077-4d00-ad56-b89eec15e730", "timestamp": 1698841238.851668 }, "facts": [{ "name": "User Elevated Action", "human": "Application used deprecated elevation API", "severity": 0, "actions": [{"name": "Report"}] }], "severity": 0 }, "related": { "users": [ {"uid": 0, "name": "root"}, {"uid": 501, "name": "local-admin"} ], "processes": [{ "pid": 3136, "name": "security_authtrampoline", "path": "/usr/libexec/security_authtrampoline", "args": ["/usr/libexec/security_authtrampoline", "/Library/Application Support/JAMF/Remote Assist/Wipe", "auth 16"], "signingInfo": { "appid": "com.apple.security_authtrampoline", "status": 0, "signerType": 0, "authorities": ["Software Signing", "Apple Code Signing Certification Authority", "Apple Root CA"] } }] }, "eventType": "GPProcessEvent" } ``` -------------------------------- ### Detect Hidden Account Creation via DSCL Source: https://context7.com/jamf/jamfprotect/llms.txt This analytic monitors process events for the dscl utility to detect the creation of hidden user accounts. It checks for specific flags indicating account concealment. ```yaml name: HiddenAccountCreatedDscl uuid: 7ee555e8-d911-4918-a9f1-c919675d02c5 inputType: GPProcessEvent filter: > $event.type == 1 AND ($event.process.path.lastPathComponent == "dscl" AND ((ANY $event.process.args == "IsHidden") AND (ANY $event.process.args == "-create") AND (ANY $event.process.args IN {"true", "1", "yes"}))) ``` -------------------------------- ### Check for Jamf Protect Quarantined Files using Jamf Pro Extension Attribute Source: https://context7.com/jamf/jamfprotect/llms.txt This bash script functions as a Jamf Pro Extension Attribute to report whether any files are present in Jamf Protect's quarantine directory. It checks the '/Library/Application Support/JamfProtect/Quarantine' directory and outputs 'Yes' if files are found, and 'No' otherwise. ```bash #!/bin/bash # Extension Attribute: Jamf Protect Quarantined Files # Data Type: String # Input Type: Script QUARANTINE_FILES=$(/bin/ls /Library/Application\ Support/JamfProtect/Quarantine) if [[ -z "$QUARANTINE_FILES" ]]; then echo "No" else echo "Yes" fi ``` -------------------------------- ### Export Alert Data with Severity Filtering Source: https://context7.com/jamf/jamfprotect/llms.txt This section demonstrates how to use the 'listAlerts' GraphQL query to retrieve security alerts. It includes filtering by severity level (e.g., Low to High) and paginating through results. ```APIDOC ## Export Alert Data with Severity Filtering ### Description Retrieves security alerts from Jamf Protect, with the ability to filter by minimum and maximum severity levels. Results include alert details, severity, associated computer hostname, and creation timestamp. ### Method POST ### Endpoint `https://{your-tenant}.protect.jamfcloud.com/graphql` ### Parameters #### Query Parameters (within the GraphQL query) - **min_severity** (SEVERITY) - Optional - The minimum severity level to filter alerts (e.g., "Informational", "Low", "Medium", "High"). - **max_severity** (SEVERITY) - Optional - The maximum severity level to filter alerts. - **page_size** (Int) - Optional - The number of items to return per page. - **next** (String) - Optional - A token for fetching the next page of results. #### Request Body (GraphQL Payload) - **query** (string) - Required - The GraphQL query string for `listAlerts`. - **variables** (object) - Optional - Variables for the query, including severity filters and pagination tokens. ### Headers - **Authorization** (string) - Required - The Bearer access token. ### Request Example ```python import json from datetime import datetime # Assume PROTECT_INSTANCE and access_token are defined from the authentication step MIN_SEVERITY = "Low" MAX_SEVERITY = "High" JSON_OUTPUT_FILE = f"Jamf_Protect_Alerts_{datetime.utcnow().strftime('%Y-%m-%d')}.json" LIST_ALERTS_QUERY = """ query listAlerts( $min_severity: SEVERITY $max_severity: SEVERITY $page_size: Int $next: String ) { listAlerts( input: { filter: { severity: { greaterThanOrEqual: $min_severity } and: { severity: { lessThanOrEqual: $max_severity } } } pageSize: $page_size next: $next } ) { items { json severity computer { hostName } created } pageInfo { next } } } """ # Example of making the call and handling pagination (similar to listComputers) # alerts = [] # next_token = None # while True: # vars = {"min_severity": MIN_SEVERITY, "max_severity": MAX_SEVERITY, "page_size": 100, "next": next_token} # resp = make_api_call(PROTECT_INSTANCE, access_token, LIST_ALERTS_QUERY, vars) # next_token = resp["data"]["listAlerts"]["pageInfo"]["next"] # alerts.extend(resp["data"]["listAlerts"]["items"]) # if next_token is None: # break # print(f"Found {len(alerts)} alerts") # with open(JSON_OUTPUT_FILE, 'w') as f: # json.dump(alerts, f, indent=2) ``` ### Response #### Success Response (200) - **data.listAlerts.items** (array) - An array of alert objects. - **json** (object) - The full JSON payload of the alert. - **severity** (string) - The severity level of the alert (e.g., "Low", "Medium", "High"). - **computer.hostName** (string) - The hostname of the computer the alert is associated with. - **created** (string) - The timestamp when the alert was created (ISO 8601 format). - **data.listAlerts.pageInfo.next** (string) - A token for the next page of results, or null if it's the last page. #### Response Example ```json { "data": { "listAlerts": { "items": [ { "json": { ... alert details ... }, "severity": "Medium", "computer": { "hostName": "Developer-Mac.local" }, "created": "2023-10-27T11:30:00Z" } ], "pageInfo": { "next": "another_next_token_or_null" } } } } ``` ``` -------------------------------- ### Detect Non-Writable USB Insertion Source: https://context7.com/jamf/jamfprotect/llms.txt This analytic identifies the insertion of removable storage devices that are marked as non-writable. It leverages USB event types to trigger alerts. ```yaml name: UsbInsertionNonWritable uuid: 28043b0a-7cd1-45b1-a128-148b96b7b6c7 inputType: GPUSBEvent filter: > $event.type == 0 AND $event.device.removable == 1 AND $event.device.writable != 1 actions: - name: Log ```