Skip to content

Rule Schema

whac embeds this schema in the binary. This page shows the file internal/rules/builtin/schema.cue from the repository.

Field Type Description
rules map of #SecretRule The rules. The field name is the rule ID.
overrides map of #Override Disable or re-grade a rule by ID.
config #Config Scan settings.
allowlist #Allowlist Suppressions for false positives.
Field Default
kind "pattern"
severity "HIGH"
enabled true
secretGroup 1
entropy.minLen 16
config.maxFileSize 2097152 (2 MiB)
internal/rules/builtin/schema.cue
// Whac-a-Leak rule and configuration schema.
//
// This file and the rule files next to it are embedded in the whac binary.
// User layers in .whac/*.cue (package whac) are unified with them at load
// time; see .okfw/specs/rule-schema for the semantics. internal/rules/spec.go
// mirrors these definitions for decoding.
package whac
#Severity: "LOW" | "MEDIUM" | "HIGH" | "CRITICAL"
// A rule sample: plain text, or text with a file name. The file name picks
// the structural extractor, e.g. {file: "config.yaml", content: "..."}.
// Plain text is scanned as an unstructured text file.
#Sample: string | {file: string & !="", content: string}
#Charset: "hex" | "alnum" | "base64" | "base64url"
#SecretRule: {
id: =~"^[A-Z][A-Z0-9_]*$"
description: string & !=""
kind: *"pattern" | "assignment"
severity: *"HIGH" | #Severity
enabled: *true | bool
// Literal keywords for the Aho-Corasick prefilter, matched
// case-insensitively. Pattern rules need at least one. An assignment rule
// runs on unstructured text only on lines that hold one of its keywords;
// without keywords it runs on structured files only.
keywords: [...(string & !="")]
// RE2 syntax (Go regexp). Required for pattern rules. For an assignment
// rule it is optional and runs on the value.
regex?: string
// Capture group holding the secret; 0 means the whole match.
secretGroup: *1 | (int & >=0)
// The engine evaluates the regex on each line that holds a keyword. Set
// multiline for secrets that span lines, such as PEM keys: the window then
// runs up to 64 KiB from the start of the keyword's line.
multiline: *false | bool
// Assignment rules: a regex on the last key segment and/or doublestar
// globs over the dotted key path ("**.credentials.password"). When a rule
// sets both, both must match. Path matching ignores case.
keyContext?: string
paths?: [...string]
entropy?: {
// Bits per character. When min is omitted, the engine derives a
// length- and charset-aware default (ADR-0006).
min?: number & >=0 & <=8
max?: number & >=0 & <=8
minLen: *16 | (int & >0)
charset?: #Charset
if min != _|_ if max != _|_ {max: >=min}
}
// Case-insensitive substrings that, when present in the secret, drop it.
stopwords: [...(string & !="")]
// Secrets matching any of these regexes are dropped.
allowRegexes: [...string]
tests: {
// Each sample must produce a finding. Use obviously fake values.
mustMatch: [#Sample, ...#Sample]
// Each sample must produce no finding (often: matches the regex but
// is rejected by entropy, placeholders, stopwords or allowRegexes).
mustIgnore: [...#Sample]
}
if kind == "pattern" {
keywords: [string, ...string]
regex: string
// Cheap author-side check under `cue vet`; the authoritative check
// runs every sample through the real engine (ADR-0009).
tests: mustMatch: [...((string & =~regex) | {file: string, content: =~regex})]
}
}
// Re-grading or disabling a rule from a layer. A rule's own severity is
// concrete, so it cannot be changed by unification; overrides can.
#Override: {
severity?: #Severity
enabled?: bool
}
#Config: {
// Files larger than this many bytes are skipped.
maxFileSize: *2097152 | (int & >0)
// Doublestar globs → reason.
exclude: [Glob=string]: string & !=""
// Key-path globs → reason. The builtin SENSITIVE_PATH rule reports any
// value at these paths in structured files, even with low entropy.
sensitivePaths: [Glob=string]: string & !=""
// Agent-context preflight (ADR-0011): well-known credential files,
// relative to $HOME and to each --root. Keyed by ID, so layers can add
// entries or disable one with `enabled: false`.
credentialPaths: [ID=string]: #CredentialPath
// Reachable access to credentials, reported as exposure findings.
exposure: {
sshAgent: {severity: *"HIGH" | #Severity, enabled: *true | bool}
dockerSocket: {severity: *"CRITICAL" | #Severity, enabled: *true | bool}
// Runs only with --probe-imds: the one network request whac makes.
cloudMetadata: {severity: *"HIGH" | #Severity, enabled: *true | bool}
}
}
#CredentialPath: {
// Doublestar glob relative to the root, e.g. ".aws/sso/cache/*.json".
glob: string & !=""
description: string & !=""
// Omit for every operating system.
os?: "linux" | "darwin" | "windows"
// Forces a structural extractor when the file name has no extension.
format?: "yaml" | "json" | "dotenv" | "properties" | "ini" | "hcl"
// Larger files are skipped. Shell histories need more than the default.
maxSize: *1048576 | (int & >0)
enabled: *true | bool
}
#Allowlist: {
// whac1 fingerprint → reason. Only for false positives: real secrets
// get rotated, never allowlisted (ADR-0007).
fingerprints: [=~"^whac1:[0-9a-f]{64}$"]: string & !=""
// Doublestar path globs → reason.
paths: [Glob=string]: string & !=""
// Regexes over the secret → reason.
regexes: [Re=string]: string & !=""
}
rules: [ID=string]: #SecretRule & {id: ID}
overrides: [ID=string]: #Override
config: #Config
allowlist: #Allowlist