CLI
Scanner provides a Python CLI for validating, testing, and syncing detection rule YAML files. The package is published as scanner-cli on PyPI.
Installation
We recommend installing scanner-cli with pipx, which puts the CLI in its own isolated virtual environment and on your PATH:
pipx install scanner-cliInstalling pipx
If you don't already have pipx, install it via one of the following.
brew install pipx
pipx ensurepathsudo apt install pipx
pipx ensurepathpython3 -m pip install --user pipx
python3 -m pipx ensurepathRestart your shell after pipx ensurepath so the updated PATH takes effect. See the pipx installation docs for additional platforms.
Authentication
You will need the API URL of your Scanner instance and an API key. Find both under Settings > API Keys.
Either export them as environment variables:
export SCANNER_API_URL=<Scanner API URL>
export SCANNER_API_KEY=<Scanner API key>or pass them on every invocation:
Validate detection rule
To validate files:
To validate directories:
Only YAML files with the correct schema header will be validated. Multiple files or directories can be provided. To recursively search through directories, use the recursive flag -r.
validate checks that the specified files are valid Scanner detection rule files. After validating files, use run-teststo check that all tests pass before syncing to Scanner.
Example

Run detection rule tests
To run detection rule tests on files:
To run detection rule tests on directories:
This will only run tests on YAML files with the correct schema header. Multiple files or directories can be provided. To recursively search through directories, use the recursive flag -r.
Example

Sync an entire repository (sync-git-repo)
sync-git-repo)sync-git-repo is the CLI implementation of user-pushed sync. It uploads the tracked files in a Git working tree (via git ls-files) to a push sync source on Scanner by calling POST /v1/upload_git_repo_zipball. The server runs the same validate / test / materialize pipeline as a GitHub-polled sync, so a successful upload is equivalent to a poll-mode sync cycle — including deleting rules whose YAML has been removed from the repository.
If your rules are on GitHub.com or GitHub Enterprise Server, the
scanner-inc/sync-detection-rulesGitHub Action wraps this command and surfaces failures as PR annotations. See getting-started.md → user-pushed sync. The notes below describe the underlyingscanner-clicommand.
To run a sync from the current directory:
To sync a different working tree, pass it as a positional argument:
<push-key> identifies the push sync source on Scanner — find or create one under Detections > Synced Repositories. The current branch (the working tree must not be in a detached-HEAD state), commit SHA, and HEAD commit message (git log -1 --pretty=%B) are derived from the repo and sent with the upload; a dirty working tree is allowed and reported with a +dirty suffix on the commit SHA. There's no --branch flag — the branch is always the one currently checked out, and the upload targets the sync source whose (push_key, branch) matches. If no source is registered for the current branch, the upload is rejected.
If any rule in the upload fails to validate or its tests fail, the server rejects the whole upload — no rules from it are synced. The CLI prints the per-file failures so you can fix them and re-run.
Sync individual rules (sync)
sync)Prefer sync-git-repo (or GitHub-polled sync) for normal flows: both reconcile the whole set of rules in a sync source on every run. Per-file sync only creates and updates rules — it has no way to delete a rule that's been removed from the repo — and shouldn't be mixed with the repo-level flows on the same rules.
-f, -d <dir>, and -r (recursive) work the same way as validate and run-tests. --team-id is the Scanner team to sync into (see Settings > General). --sync-config-file points at a YAML file containing the event_sink_keys → event_sink_ids mapping that resolves any event_sink_keys referenced by your rules; pass an empty file if your rules don't use event-sink keys.
Each rule's YAML must include a push_key field at the top level — that field is the per-rule identifier the server uses to look up an existing detection rule to update (or create a new one if none matches). See the detection rule schema for the field's exact placement.
Failing tests
Tests fail if:
The
expected_detection_resultis true and the provided log events do not trigger a detection in the specified time windowThe
expected_detection_resultis false and the provided log events trigger a detection in the specified time window
Tips for debugging failed tests:
Check that the log events are hits for
query_textCheck that the log events have timestamps that fall in the time window specified by
now_timestamp,time_range_s, andrun_frequency_sIf
now_timestampis not provided, the timestamp of the latest log event is used. In both cases, the timestamps are rounded to the nextrun_frequency_s.Example: if
time_range_sis 600 seconds,run_frequency_sis 60 seconds, andnow_timestampis2024-07-05T10:15:00.000Z, the time window specified is2024-07-05T10:05:00.000Zto2024-07-05T10:15:00.000Z. Log events must have timestamps in this time window in order to trigger an alert.
Last updated
Was this helpful?