Skip to content

Rule Authoring

Picket’s compatibility rule format is Gitleaks TOML. Native rule-pack metadata extends this model, but strict compatibility mode must keep Gitleaks behavior stable.

Compatibility commands load configuration in this order:

  1. --config
  2. GITLEAKS_CONFIG
  3. GITLEAKS_CONFIG_TOML
  4. {target}/.gitleaks.toml
  5. embedded Gitleaks compatibility rules

Native commands currently use the same rule model with Picket-native environment precedence. picket rules check uses native precedence by default and also accepts --profile picket explicitly. Gitleaks-compatible git, dir/file/directory, and stdin scans use this same native precedence only when --profile picket is supplied.

  1. --config
  2. PICKET_CONFIG
  3. PICKET_CONFIG_TOML
  4. GITLEAKS_CONFIG
  5. GITLEAKS_CONFIG_TOML
  6. {target}/.gitleaks.toml
  7. embedded Picket default config, which extends the embedded Gitleaks compatibility rules and adds picket-default coverage

Strict compatibility commands ignore PICKET_CONFIG and PICKET_CONFIG_TOML. Native commands also understand optional Picket metadata fields on each rule.

File-backed config loads, including [extend] path, are capped at 10 MiB per file. Resolved configs are capped at 10,000 rules. Inline GITLEAKS_CONFIG_TOML and PICKET_CONFIG_TOML values are parsed from the environment value already supplied to the process.

In strict compatibility mode, [extend] path follows the pinned Gitleaks/Viper local-file behavior: absolute paths are accepted, and relative paths resolve from the process current working directory. Picket keeps that behavior for compatibility, with the byte cap, extend-depth cap, and cycle detection described above. Treat configs with local extend.path values as trusted scanner configuration rather than as scan-root-confined input.

The embedded native default combines gitleaks with the high-confidence picket-default pack. Current native defaults include Anthropic OAuth and Claude Code session credentials; legacy, project, project service-account, and organization-admin OpenAI API keys; OpenAI Codex OAuth credentials; Cast AI, Groq, and xAI keys; AWS key pairs; Azure Storage connection strings; credentialed database URLs; Google API keys and GCP service-account keys; Sourcegraph tokens; GitHub token families; Docker registry auth; private JWKs; Kubernetes Secrets; MCP server environment credentials; and npm tokens and basic credentials. Provider-specific fields receive offline structural validation where a deterministic validator exists.

The database URL rule requires a known database scheme, a username, and an embedded password; passwordless URLs are skipped. Native GitHub token rules replace inherited Gitleaks GitHub token rules so native scans emit Picket-owned IDs and metadata without duplicate findings. The native Sourcegraph rule replaces the inherited broad Sourcegraph rule so native scans do not report arbitrary 40-hex commit IDs as access tokens.

--rule-pack picket-strict adds broader medium-confidence rules for semicolon-delimited connection-string passwords, HTTP Basic authorization values, and Azure SAS signatures. --rule-pack picket-experimental adds low-confidence opaque bearer-token and session-cookie detectors under active tuning. Repeat --rule-pack to add both. These packs are opt-in and do not change the native default or strict Gitleaks compatibility behavior. Compatibility commands require --profile picket before accepting either pack.

Native .cs scans evaluate deterministic C# string-literal concatenations before matching, so literal-only string.Concat(...) calls and binary + literal chains can produce findings with csharp-string-concat decode provenance. Picket-native picket-* rule packs do not inherit Gitleaks compatibility global allowlists; broad compatibility stopwords must not suppress native hosted-scanner parity findings.

Target-local and environment configs replace the embedded native default. Use [extend] useDefault = true in a custom config to add local rules over the Gitleaks compatibility default. Explicit --rule-pack selections layer over the resolved native config.

Supported rule fields:

  • id: stable rule identifier.
  • description: human-readable finding description.
  • regex: content pattern. Empty is valid only when path is present.
  • path: path pattern.
  • secretGroup: capture group containing the secret. 0 means automatic first non-empty capture behavior.
  • entropy: minimum Shannon entropy. 0 disables entropy filtering.
  • randomnessThreshold: minimum native p(random) score from 0.0 through 1.0. 0 disables score filtering. See Randomness Scoring.
  • detector: stable built-in structured detector name. The regex and keywords remain the candidate prefilter; the detector parses the selected input and returns exact evidence spans.
  • prefilter: optional native source predicate. A result of true skips the source for a top-level predicate or skips the owning rule for a rule predicate.
  • filter: optional native finding predicate. A result of true suppresses the candidate.
  • keywords: case-insensitive prefilter terms.
  • tags: classification labels.
  • skipReport: run supporting detection without reporting normal findings.
  • severity: native report severity. Defaults to critical.
  • confidence: native report confidence. Defaults to high.
  • rulePack: native rule-pack identifier such as gitleaks, picket-default, or picket-strict.
  • provider: owning provider or credential family.
  • documentationUrl: rule documentation or remediation URL.
  • validation: stable validation template identifiers supported by the rule. Identifiers name existing offline or live validators; they do not trigger network calls by themselves.
  • revocation: stable revocation template identifiers supported by the rule. Identifiers name report/analyze templates; revocation is never automatic during scan.
  • deprecated: true when the rule remains loadable but should not be used for new rule packs.
  • examples: positive examples that must produce findings for this rule during rule QA.
  • negativeExamples: negative examples that must not produce findings for this rule during rule QA.

Example:

[[rules]]
id = "sample-token"
description = "Sample token"
regex = '''token-[0-9]+'''
keywords = ["token"]
tags = ["example"]
severity = "high"
confidence = "medium"
rulePack = "picket-default"
provider = "example"
documentationUrl = "https://example.invalid/rules/sample-token"
examples = ["token-12345"]
negativeExamples = ["token-value"]

Randomness thresholds are native-only. A positive threshold suppresses a finding when its score is lower than the configured value; strict compatibility scans ignore the field. Keep the default of zero until reviewed positive and negative examples establish a safe threshold for that specific rule.

Built-in structured detector identifiers are:

  • codex-credentials
  • docker-registry-credentials
  • gcp-service-account-key
  • jwk-private-key
  • kubernetes-secret
  • mcp-server-credentials
  • npm-credentials
  • password-assignment

Structured detectors are native-only. JSON, YAML, and npmrc parse products are bounded and shared for one input so multiple rules do not repeatedly parse the same content. Unknown detector names fail config validation.

The password-assignment detector recognizes bounded password and passwd assignments without restricting the value alphabet. The picket-generated-password rule combines that explicit context with a reviewed randomness threshold. This keeps generated values containing punctuation or ordinary stopwords detectable without broadening the compatibility-derived generic API-key rule.

prefilter and filter are Picket-native controls. Strict Gitleaks-compatible commands parse and preserve these fields but never validate or evaluate them. Use picket rules check, picket rules test, or a native scan to validate predicate syntax.

A top-level prefilter runs once for each source before rule regexes run. A rule prefilter runs before its owning rule. A top-level filter applies to every candidate finding, while a rule filter applies only to candidates from that rule. Filters run after finding metadata and optional randomness scoring are available and before required-rule correlation. When both global and rule filters exist, either one can suppress the candidate.

The expression grammar is deliberately closed:

  • boolean operators: !, &&, and ||
  • grouping with parentheses
  • equality operators: == and !=
  • numeric operators: <, <=, >, and >=
  • string operators: contains, starts_with, and ends_with
  • Scout-backed regex search with matches; its right operand must be a string literal and is compiled once during rule validation
  • finite numeric, boolean, single-quoted string, and double-quoted string literals

! binds before comparisons, comparisons bind before &&, and && binds before ||. Boolean evaluation short-circuits. String comparisons and string operators are ordinal and case-sensitive. For string-list fields, contains tests exact list membership.

Prefilters can read:

Field Type Value
source.path string Logical source path stored in findings
source.symlink string Symbolic-link display path, or an empty string

Filters can read those source fields plus:

Field Type
finding.rule_id string
finding.description string
finding.match string
finding.secret string
finding.line string
finding.start_line number
finding.end_line number
finding.start_column number
finding.end_column number
finding.entropy number
finding.randomness_score number
finding.decode_depth number
finding.is_decoded boolean
finding.tags string list
finding.decode_path string list
finding.severity string
finding.confidence string
finding.rule_pack string
finding.provider string

Example:

prefilter = 'source.path starts_with "vendor/"'
[[rules]]
id = "sample-token"
description = "Sample token"
regex = '''token-[0-9]+'''
keywords = ["token"]
prefilter = 'source.path ends_with ".generated.cs"'
filter = '''
finding.provider == "example" &&
finding.secret matches "^token-0+$"
'''

Predicate compilation rejects expressions that exceed any of these limits:

Resource Limit
Expression text 4,096 UTF-8 bytes
Tokens 256
Parenthesis nesting 16
Compiled instructions 512
String literal 1,024 UTF-8 bytes
Compiled regexes 32

Evaluation uses a 64-value stack, accepts at most 65,536 characters from one dynamic string, and examines at most 256 items from a string list. If source or finding data exceeds an evaluation limit, the expression returns false. This fail-open behavior prevents a resource-limit condition from hiding a finding. Predicates have no field, operator, or function that can access the filesystem, environment, network, reflection, or runtime code generation.

Predicate source participates in the compiled rule-set fingerprint. Predicate-bearing rule sets use path-sensitive native cache addressing so a result cannot be reused for another logical source path.

Supported validation template identifiers are:

  • offline:anthropic-oauth-token
  • offline:aws-access-key-id
  • offline:aws-access-key-pair
  • offline:azure-storage-connection-string
  • offline:buildkite-token
  • offline:cast-ai-api-key
  • offline:claude-code-session-url
  • offline:codex-access-token
  • offline:codex-refresh-token
  • offline:database-connection-url
  • offline:docker-hub-access-token
  • offline:docker-registry-auth
  • offline:gcp-api-key
  • offline:gcp-service-account-key-json
  • offline:github-app-token
  • offline:github-classic-token
  • offline:github-fine-grained-pat
  • offline:groq-api-key
  • offline:jwk-private-key
  • offline:jwt
  • offline:jwt-base64
  • offline:kubernetes-secret
  • offline:langsmith-api-key
  • offline:mcp-server-credential
  • offline:npm-auth-token
  • offline:npm-basic-auth
  • offline:nvidia-api-key
  • offline:openai-admin-api-key
  • offline:openai-legacy-api-key
  • offline:openai-project-api-key
  • offline:openai-service-account-api-key
  • offline:openrouter-api-key
  • offline:private-key-envelope
  • offline:replicate-api-token
  • offline:sourcegraph-access-token
  • offline:tailscale-api-key
  • offline:vercel-token
  • offline:xai-api-key
  • live:github-rest-user-v1

Supported revocation template identifiers are:

  • revocation:aws-iam-access-key
  • revocation:azure-storage-account-key
  • revocation:gcp-api-key
  • revocation:gcp-service-account-key
  • revocation:github-credentials-api

picket rules check rejects a template identifier when the current verifier or analyzer cannot honor it for that rule ID.

Cast AI API keys are organization-scoped and inherit their creator’s permissions. If a key is exposed, delete it in the Cast AI console, create a replacement, and update each dependent workload. Prefer a dedicated service account with only the permissions required by that workload.

Global allowlists use [[allowlists]] or the deprecated [allowlist] form. Rule allowlists use [[rules.allowlists]] or the deprecated [rules.allowlist] form.

Supported allowlist fields:

  • description
  • condition: or, and, ||, or &&
  • commits
  • paths
  • regexTarget: secret, match, or line
  • regexes
  • stopwords
  • targetRules for global allowlists only

Deprecated singular allowlist tables cannot be mixed with plural allowlist tables in the same scope.

The native default profile uses exact contextual allowlists for public Supabase and Stripe publishable keys, Cargo checksums and revisions, and complete Kubernetes external-secret template references. Cargo suppression requires both the expected file path and checksum or revision syntax. It does not ignore Cargo files as a class. Supabase secret keys and concrete Kubernetes Secret values remain reportable.

Required rules let a primary finding require nearby supporting findings.

[[rules]]
id = "primary"
description = "Primary"
regex = '''secret-[0-9]+'''
[[rules.required]]
id = "supporting"
withinLines = 3
withinColumns = 80

Picket resolves required evidence after the original input and all enabled decoding passes have been scanned. A primary finding can therefore be satisfied by supporting evidence discovered through base64, hex, percent, Unicode, or other recursive decoding. Line and column proximity always use positions remapped to the original source. Supporting rules with skipReport = true participate in the composite match without producing their own top-level findings.

Every required rule ID must exist, and a rule must not require itself.

picket rules check validates:

  • TOML shape for the supported Gitleaks schema.
  • duplicate rule IDs.
  • missing regex/path combinations.
  • invalid regexes and secret capture groups.
  • empty keywords, tags, allowlist entries, and required-rule IDs.
  • required-rule references.
  • required positive and negative examples for Picket-native rules.
  • keyword prefilters for Picket-native content rules.
  • known built-in detector names and detector-compatible native rules.
  • native predicate syntax, field availability, operand types, regexes, and resource limits.
  • obvious Picket-native regex performance hazards such as unbounded .* or .+ spans outside character classes.
  • positive and negative examples without printing example contents in diagnostics.
  • validation and revocation template identifiers supported by the current verifier/analyzer.

picket rules test <rule-id> <input> scans sample text with one selected rule using Picket-native config precedence by default. It accepts --source for target-local .gitleaks.toml discovery, --path for path-only rules and report location metadata, --max-decode-depth, --max-target-megabytes, --ignore-gitleaks-allow, --redact[=n], and the native report formats json, jsonl, csv, junit, html, gitlab, sarif, and toon. Use -- before <input> when the sample starts with -. The default output is Picket JSON with schema, rule metadata, stable fingerprints, hashes, decode provenance, and offline validation state. Use --print-config to emit the resolved selected rule config.

Picket compiles rule and allowlist patterns to Scout ByteRegex. Unsupported patterns fail at config load with the rule ID and pattern context. Structured detector rules still use ByteRegex and keywords as their candidate prefilter; only the selected native rule then runs its bounded structured detector. Picket must not silently fall back to a different regex engine in Native AOT builds.