Configuration
Full shk.toml policy reference: scan settings, thresholds, masking, action guard, env secret stores, custom rules, and suppression.
On this page
shk reads project policy from shk.toml in the current working directory. If the file is absent, read-only commands use built-in defaults. Commands that write project or tool configuration require shk.toml. Pass the global --project-root <DIR> flag to resolve the policy from another directory.
Create a starter policy as part of the interactive first-run setup, or write only the policy file:
shk init
shk policy initCreate a stricter starter policy that fails on medium-severity findings:
shk init --strict
shk policy init --strictPolicy Reference
Default policy shape, plus an optional secrets push profile example:
[scan]
include = ["**/*"]
exclude = [
".git/**",
"node_modules/**",
"dist/**",
"build/**",
"coverage/**",
"**/*.svg",
"**/*.png",
"**/*.jpg",
"**/*.jpeg",
"**/*.gif",
"**/*.webp",
"**/*.ico",
"**/*.icns",
"**/*.avif",
"**/*.bmp",
"**/*.tif",
"**/*.tiff",
"**/*.mp4",
"**/*.m4v",
"**/*.mov",
"**/*.webm",
"**/*.mkv",
"**/*.avi",
"**/*.ogv",
"**/*.mp3",
"**/*.m4a",
"**/*.wav",
"**/*.flac",
"**/*.aac",
"**/*.ogg",
"**/*.opus",
"**/*.woff",
"**/*.woff2",
"**/*.ttf",
"**/*.otf",
"**/*.eot"
]
max_file_size_bytes = 1048576
binary_detection_bytes = 8192
follow_symlinks = false
include_binary = false
[rules]
secrets = true
pii = true
pii_languages = ["en", "ja"]
env = true
internal_terms = false
ai_context = true
[thresholds]
default_fail_on = "high"
scan_fail_on = "high"
pre_commit_fail_on = "high"
[mask]
mode = "strict"
min_severity = "medium"
redaction = "match"
# preserve_prefix = 4
# preserve_suffix = 4
[action_guard]
enabled = true
profile = "recommended"
allow = []
deny = []
[doctor.ignore]
required_patterns = [
".env",
".env.*",
"!.env.example",
"secrets/**",
"credentials/**",
"*.pem",
"*.key",
"*.p12",
"*.mobileprovision",
"*.log",
"*.shk-map"
]
# Optional profiles for `shk secrets push`.
[secrets.profiles.prod]
provider = "aws"
mode = "blob"
target = "app/prod/dotenv"
source = ".env.production"
audit = true
confirm = true
# Optional env secret store (default: OS keyring).
# [env]
# secret_store = "keyring" # keyring | 1password
# project_id = "acme/backend-api" # required when secret_store = "1password"
# [env.onepassword]
# vault = "shk-project-keys" # required when secret_store = "1password"shk init --strict uses the same structure but sets default_fail_on, scan_fail_on, and pre_commit_fail_on to medium.
Scan Settings
| Key | Default | Behavior |
|---|---|---|
include |
["**/*"] |
Glob patterns included in scans. |
exclude |
Built-in generated file and media exclusions | Glob patterns excluded from scans. |
max_file_size_bytes |
1048576 |
Files larger than this limit are skipped. |
binary_detection_bytes |
8192 |
Number of leading bytes inspected for binary detection. |
follow_symlinks |
false |
Whether scanner traversal follows symlinks. |
include_binary |
false |
Whether binary-looking files are scanned instead of skipped. |
Only an absent key falls back to the default. include = [] scans nothing and exclude = [] disables the built-in exclusions.
Supported document formats (.docx, .xlsx, .pptx, and text-layer .pdf) are text-extracted before binary skipping. Office findings are labelled with internal entry paths such as report.docx:word/document.xml; PDF findings use the PDF path itself. Image-only PDFs are not OCRed and produce scan.document_text_empty when no text can be extracted.
Rule Settings
| Key | Default | Behavior |
|---|---|---|
secrets |
true |
Enables built-in secret rules. |
pii |
true |
Enables built-in PII rules. |
pii_languages |
["en", "ja"] |
Enables language-gated PII rules. Universal PII rules run when pii = true. |
env |
true |
Enables env-related rules such as env.sensitive_assignment (dotenv-style assignments of sensitive variable names with non-placeholder values). Env rules only apply to dotenv-style files (.env, .env.local, dev.env, ...); .env.example and .env.sample files are always skipped. |
internal_terms |
false |
Enables custom rules with kind = "internal". |
ai_context |
true |
Enables high-signal AI context safety rules for Unicode controls and unsafe URI schemes. |
Thresholds
Valid severity values are info, low, medium, high, and critical.
| Key | Default | Behavior |
|---|---|---|
default_fail_on |
high |
Fallback threshold. |
scan_fail_on |
high |
Threshold for normal scans. |
pre_commit_fail_on |
high |
Threshold for shk scan --staged and Cursor pre-hook scans. |
The --fail-on CLI option overrides the configured threshold for that command invocation.
Mask Settings
| Key | Default | Behavior |
|---|---|---|
mode |
strict |
Only strict is supported. Other values are rejected. |
min_severity |
medium |
Minimum finding severity to redact. Use info, low, medium, high, or critical. |
redaction |
match |
match redacts only matched values; full redacts entire lines; partial preserves configured matched-value edges. |
preserve_prefix |
4 |
Characters preserved at the start of a matched value when redaction = "partial". |
preserve_suffix |
4 |
Characters preserved at the end of a matched value when redaction = "partial". |
Action Guard Settings
action_guard applies only to blocking pre-hook scans such as shk scan --hook-mode claude-code. It checks operation intent before content scanning, and it is skipped in --audit mode and in --post hooks.
| Key | Default | Behavior |
|---|---|---|
enabled |
true |
Enables action guard blocking in pre-hook mode. |
profile |
recommended |
Built-in coverage level: minimal, recommended, or strict. Any other value falls back to recommended. |
allow |
[] |
Action patterns that bypass action guard, for project-approved operations. |
deny |
[] |
Extra project-specific action patterns to block. |
Action patterns use tool-like strings with * wildcards, such as Bash(psql:*), Bash(kubectl delete:*), Read(.env), or Write(tokens/*.json). allow is checked before built-in and custom deny rules.
Shell commands are evaluated segment by segment. A single-command allow such as Bash(echo:*) does not allow a later segment in echo ok && curl ...; allow the complete compound command explicitly only when every segment is trusted. This prevents a broadly allowed first command from hiding a dangerous later command.
The strict profile also blocks opaque execution forms such as bash -c, sh -c, zsh -c, python -c, python3 -c, node -e, node --eval, ruby -e, and perl -e instead of trying to fully interpret embedded scripts.
Pseudonymize Settings
[pseudonymize] configures shk mask --pseudonymize. Unknown keys are rejected, and invalid settings exit 2 before any file is written.
| Key | Default | Behavior |
|---|---|---|
norm |
"v1" |
Normalization version applied before hashing. Only v1 is accepted. |
token_bits |
64 |
Token width in bits. Must be a multiple of 8 between 64 and 128. |
email_strip_subaddress |
false |
Drop +tag sub-addresses before tokenizing email addresses, so a+news@example.com and a@example.com share a token. |
columns |
{} |
Table-mode defaults mapping a header name to a kind (email, phone, name, or custom:<label>). Entries that match no header are ignored, and --columns overrides them. |
rules |
{} |
Text-mode overrides mapping a rule id to a kind, for detections that should become tokens instead of [REDACTED]. |
[pseudonymize]
token_bits = 64
email_strip_subaddress = false
[pseudonymize.columns]
Email = "email"
Phone = "phone"
Name = "name"The project key lives in the same backend as env keys ([env].secret_store); see shk pseudonymize.
Env Secret Store
shk env stores dotenv private keys outside the repository. By default it uses the OS keyring (macOS Keychain, Windows Credential Manager, or Linux Secret Service / keyutils). Teams can opt in to 1Password for shared vault distribution, centralized revocation, and Business audit logs.
The 1Password backend requires the 1Password CLI (op), version 2.24.0 or later. Install and sign in to op before enabling the backend. Confirm the setup with:
op --version
op whoami
shk doctor envThe default keyring backend does not require the 1Password CLI.
# Default: local OS keyring (no extra dependencies).
[env]
secret_store = "keyring"# Opt in to 1Password for team vault sharing.
# Keep secret_store = "keyring" until `shk env key migrate --to 1password` succeeds.
[env]
secret_store = "1password"
project_id = "acme/backend-api" # required; machine-independent identifier
[env.onepassword]
vault = "shk-project-keys" # required before using or migrating to 1Password| Key | Default | Behavior |
|---|---|---|
env.secret_store |
"keyring" |
Backend for native env keys, imported dotenvx keys, and pseudonymize keys. Supported values: keyring, 1password. |
env.project_id |
unset | Required when secret_store = "1password". Stable, machine-independent project label used in 1Password item titles. Must not contain : or leading/trailing whitespace. shk doctor env can suggest a value from git remote get-url origin or the repository directory name. |
env.onepassword.vault |
unset | Required when secret_store = "1password". Vault name passed to the 1Password CLI (op). |
1Password item titles follow shk:{project_id}:{segment}:{key} where {segment} is env for native keys (shk env encrypt, shk env key import), dotenvx for imported dotenvx keys, or pseudonymize for shk mask --pseudonymize keys. Items are tagged shk. Raw key values never appear in shk.toml, JSON reports, or .shk/audit.log.
Set SHK_OP_PATH to an absolute path when the op binary is not on PATH. Resolution order is SHK_OP_PATH, known install paths, then PATH (the last option is flagged in shk doctor env because it is susceptible to hijacking). The 1Password CLI must be signed in (op whoami) and at least version 2.24.0.
Keyring storage uses three service names in the OS credential store:
| Service | Used by |
|---|---|
security-harness-kit/env |
Native keys from shk env encrypt, shk env key import, and adopted dotenvx keys |
security-harness-kit/dotenvx |
Keys imported with shk env dotenvx import-keys |
security-harness-kit/pseudonymize |
Project keys for shk mask --pseudonymize and shk pseudonymize |
Run shk doctor env to check plaintext env files and, when 1Password is configured, op resolution, version, and sign-in state.
Secret Manager Profiles
[secrets.profiles.<name>] stores reusable defaults for shk secrets push. CLI flags override profile values.
Blob mode stores the whole dotenv file as one provider secret:
[secrets.profiles.prod]
provider = "aws"
mode = "blob"
target = "app/prod/dotenv"
source = ".env.production"
region = "ap-northeast-1"
audit = true
confirm = true
create_if_missing = false
expected_env = "production"Per-key mode stores each dotenv key under a target prefix:
[secrets.profiles.prod-keys]
provider = "gcp"
mode = "per-key"
target_prefix = "app/prod/"
source = ".env.keys"
project = "my-gcp-project"
location = "global"
audit = trueSupported profile keys:
| Key | Behavior |
|---|---|
provider |
aws or gcp. |
mode |
blob or per-key (per_key is accepted as an alias). Defaults to blob when omitted. |
target |
Blob mode target secret name. |
target_prefix |
Per-key mode target prefix. |
source |
Source dotenv file, resolved relative to the Git repository root (or the current directory outside a repository) when relative. |
region |
AWS region. Otherwise AWS CLI environment/config is used. |
project |
GCP project. Otherwise gcloud environment/config is used. |
location |
GCP location. Defaults to global. |
audit |
Append metadata-only .shk/audit.log entries when true. |
confirm |
Prompt before writing when true. The prompt needs a terminal; non-interactive runs exit 2 unless --yes or --dry-run is passed. |
create_if_missing |
Create provider secrets when missing. |
expected_env |
Lint hint used for environment-like values such as NODE_ENV. |
Unknown profile fields are rejected. Raw secret values must not be placed in shk.toml; keep values in the source dotenv file or provider secret manager.
Custom Rules
Add [[custom_rules]] entries for project-specific confidential words, codenames, or regex patterns:
[[custom_rules]]
id = "internal.codename"
pattern = "ProjectNebula|CONFIDENTIAL_CLIENT_X"
severity = "high"
kind = "internal"
message = "Internal confidential term detected"
case_insensitive = false
enabled = trueFields:
| Field | Default | Behavior |
|---|---|---|
id |
Required | Stable rule identifier. |
pattern |
Required | Rust regex pattern. |
severity |
medium |
Finding severity. |
kind |
internal |
Finding kind. |
message |
Custom sensitive term detected |
Finding message. |
confidence |
1.0 |
Finding confidence. |
case_insensitive |
false |
Wraps the pattern in case-insensitive matching. |
enabled |
true |
Enables or disables the rule. |
Custom rules participate in scan, mask, hook mode, inline suppression, and [[allowlist]].
Suppression
Inline suppression is available in files that support comments:
API_KEY=synthetic-example-value # shk-ignore secret.generic_api_key
# shk-ignore-next-line secret.generic_api_key
SECRET=synthetic-example-value
<!-- shk-ignore-next-line secret.generic_api_key -->
SECRET=synthetic-example-valueA marker without a rule id (# shk-ignore, # shk-ignore-next-line) suppresses every rule on that line, and // comments work the same way as #.
Policy allowlists can suppress by path and rule:
[[allowlist]]
rule_id = "secret.generic_api_key"
path = "fixtures/**"
reason = "Intentional test fixture"
expires = "2026-12-31"path is optional and defaults to **/*. expires must be YYYY-MM-DD; an entry whose date cannot be parsed never expires.
For Office document findings, match the internal entry label shown in reports:
[[allowlist]]
rule_id = "secret.openai_api_key"
path = "report.docx:word/document.xml"
reason = "Intentional document fixture"Policy allowlists can also suppress by value hash:
[[allowlist]]
rule_id = "pii.email"
value_hash = "sha256-hmac:a3f1..."
reason = "Public support address"Do not place raw secret values in shk.toml. Use value_hash for value-specific suppression. A value_hash is a deterministic fingerprint for equality checks, not cryptographic secret storage; anyone who knows the candidate value and rule id can compute the same hash.
Expired allowlist entries produce low-severity warning findings.
Doctor Ignore Settings
doctor.ignore.required_patterns controls the patterns checked by shk doctor ignore.
The default required patterns are:
[
".env",
".env.*",
"!.env.example",
"secrets/**",
"credentials/**",
"*.pem",
"*.key",
"*.p12",
"*.mobileprovision",
"*.log",
"*.shk-map"
]When shk doctor ignore --fix is used, missing required patterns are appended to .gitignore.