1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# velociredactor default configuration.
#
# This file is built into the binary. --config, then VELOCIREDACTOR_CONFIG,
# then a velociredactor.yml found in the current directory or a parent,
# each replaces it entirely: nothing is merged and nothing is inherited.
# To write your own, start from a copy of this one:
#
# velociredactor config show > my-config.yml
# Scan comments as well as values, in the formats that have them (JSON with
# comments, YAML, TOML, HCL, INI, dotenv, XML, Java properties).
comments: false
# ---------------------------------------------------------------------------
# formats — the input formats to recognize, and the order they are tried in
# when the format is guessed from content.
#
# Passing --raw skips all formats and uses plain text instead.
# ---------------------------------------------------------------------------
formats:
- json
- jsonl
- yaml
- toml
- xml
- hcl
- ini
- dotenv
- properties
- csv
- tsv
- psv
- bplist
- text
# ---------------------------------------------------------------------------
# policy — which values are looked at in the first place.
#
# Values skipped here are never scanned, so no detector below can redact them.
# ---------------------------------------------------------------------------
policy:
# Keys ending in one of these are skipped, compared case-insensitve. These are
# structural and high-entropy by nature, and redacting them breaks the
# document: identifiers, UUIDs, hashes, and the thinking-block signatures in
# LLM transcripts, which must survive intact for a transcript to replay.
skip_key_suffixes:
# Keys skipped by exact (lowercase) name. Kept separate from the suffixes
# above on purpose: as a suffix, "path" would also skip "paths" and "xpath".
skip_keys:
# Objects carrying inline image or binary payloads, identified by a field
# whose value starts with one of `prefixes` or equals one of `values`. The
# field name and its value are both compared ignoring case.
skip_object:
key: "type"
prefixes:
values:
# An object holding both a host-like and a user-like key is connection
# settings, which makes a bare `password` key inside it sensitive. Compared
# after normalizing `-`, ` ` and `.` to `_`, so "Data Source" matches
# "data_source".
credential_context:
host_keys:
user_keys:
# ---------------------------------------------------------------------------
# placeholder — values that look like credentials but are not.
#
# Documentation samples, masks, and earlier redactions, which would otherwise
# be reported as secrets over and over. Consulted by every detector that finds
# a credential by its surroundings or by a vendor's pattern, and so can be
# looking at a sample of one: ruleset, credentialed_uri, connection_string,
# credential_assignment, credential_key.
#
# The others do not consult it, and are not meant to: `entropy` judges the
# value itself, where a placeholder scores too low to report anyway, and
# `regex`, `value` and `path` were told exactly what to redact.
# ---------------------------------------------------------------------------
placeholder:
# Compared in lowercase after trimming whitespace and one layer of quotes,
# so entries must be written in lowercase.
values:
- "redacted"
- "[redacted]"
- "<redacted>"
- "changeme"
- "example"
- "placeholder"
- "your_password"
- "your_db_password"
- "your_secret"
- "secret_here"
- "password"
- "db_password"
- "secret"
- "secret_key"
- "api_key"
- "api_secret"
- "api_token"
- "api_secret_key"
# A value that is a run of one of these characters is a mask (`****`,
# `xxxx`). Written as one string, one character per mask character.
mask_characters: "*x.-"
# Shortest run that counts as a mask, so "x" alone is still a secret.
mask_min_length: 3
# Shortest `<name>` placeholder that counts, so "<a>" is still a secret.
# The interior must be lowercase words joined by `-` or `_`, which keeps
# "<hunter2>" and "<RealPassword>" secret.
bracket_min_length: 5
# ---------------------------------------------------------------------------
# detectors — what looks for secrets, in the order they are listed.
#
# Every detector that runs is listed here with its own settings; a detector
# that is not listed does not run. Detectors that take no settings are written
# as a bare name. The name of each is what `velociredactor list` reports in its
# DETECTOR column.
# ---------------------------------------------------------------------------
detectors:
# Long tokens that look random.
- entropy:
# Bits per byte required to redact a token. Ordinary words and
# identifiers fall below 4.5; API keys and tokens usually score above 5.
# Lower redacts more.
threshold: 4.5
# Used instead when the value sits under a sensitive key. Below the
# hex-alphabet ceiling of 4.0, so MD5/SHA-shaped digests still qualify.
sensitive_threshold: 3.5
# Shortest run of token characters ([A-Za-z0-9+_=-]) considered at all.
# `/` is not a token character, so a whole file path is not one token.
min_token_length: 10
# Key segments that mark a field as likely holding a secret. Matched as
# whole segments after splitting camelCase and `_`, so "apiKey" is
# sensitive but "keyboard" is not. Entries must be single lowercase
# words.
sensitive_segments:
# Key names that contain a sensitive segment but are structural rather
# than credentials. Matched exactly or as an `_`-delimited suffix, so
# "user_foreign_key" matches. These keep the ordinary threshold.
structural_keys:
- "foreign_key"
- "primary_key"
- "sort_key"
- "partition_key"
- "lookup_key"
- "cache_key"
- "public_key"
- "idempotency_key"
# Hex strings of exactly these lengths (MD5, SHA-1, SHA-256) are
# redacted under a sensitive key whatever their entropy.
hex_digest_lengths:
# betterleaks/gitleaks rules: several hundred vendor-specific secret
# formats. Findings are labelled `ruleset:<rule id>`.
- ruleset:
# Rulesets to load, in order; a later rule replaces an earlier one with
# the same id. `builtin:betterleaks` is the release vendored into this
# binary. Anything else is a path to a betterleaks/gitleaks TOML file,
# relative to this config file.
rules:
- builtin:betterleaks # See https://github.com/phayes/velociredactor/blob/master/vendor/betterleaks/betterleaks.toml
# - ./my-rules.toml
# Comments marking a line as intentionally containing a secret. Matched
# case-sensitively anywhere on the line.
allow_signatures:
# Rules to switch off, by id. No separator, so `*` matches anything:
# ["github-pat", "aws-*"].
exclude_rules:
# Every match of a regular expression, reported under `label`. List `regex`
# as many times as you like: each entry's patterns share its label, which is
# how a listing keeps unrelated groups apart. `label` may be left out, and
# is then `regex`.
#
# These patterns identify credentials by prefix and length alone, so they
# are caught whatever their entropy or surrounding key. They are not
# anchored to a word boundary, so a key glued to a preceding identifier is
# still found; the cost is that a long identifier merely starting with a
# prefix is redacted too.
- regex:
label: provider_token
patterns:
# Supabase secret API key (bypasses row-level security).
- 'sb_secret_[A-Za-z0-9_-]{20,}'
# Supabase personal access token. Publishable keys (sb_publishable_…)
# are meant to be embedded in client code and are deliberately not
# matched.
- 'sbp_[a-z0-9_-]{20,}'
# URLs with a password in the userinfo (postgres://user:pass@host/db). The
# whole URL is redacted, unless the password is a `placeholder` value.
- credentialed_uri
# JDBC URLs, libpq DSNs, and ADO.NET connection strings.
- connection_string
# DB_PASSWORD=value assignments inside free text.
- credential_assignment
# The whole value of a password field in structured data.
- credential_key
## Personal data. Off by default: these patterns are broad, and whether a
## name or an address is a secret depends on the document.
#
# - pii:email:
# # Addresses belonging to automation rather than to a person. A
# # leading `@` matches the domain suffix, a trailing `@` the
# # local-part prefix, and anything else is an exact address.
# allowlist:
# - "noreply@"
# - "actions@"
# - "info@"
# - "@users.noreply.github.com"
# - "@noreply.github.com"
#
# - pii:phone
#
# - pii:address
## OpenAI Privacy Filter: a language model that reads each value in context
## and finds names, addresses, dates, emails, phone numbers, URLs, account
## numbers, and secrets that no pattern above describes. Findings are
## labelled `privacy_filter:<category>`.
##
## It is slow next to everything else here, and heavy: loading it takes up
## to 16 GB of memory at its peak. Fetch the model (2.6 GB) first:
##
## velociredactor privacy_filter download
##
## Every setting below is optional and shown with its default, except
## `model_dir`; `- privacy_filter` alone is a complete entry.
#
# - privacy_filter:
# # The downloaded model, relative to this file. When left out, the
# # copy in the Hugging Face cache, which is where the download command
# # puts it unless given --dir: $HF_HUB_CACHE, else $HF_HOME/hub, else
# # ~/.cache/huggingface/hub.
# model_dir: ./privacy-filter
#
# # auto, cpu, cuda (the first GPU), or cuda:N. `auto` picks a CUDA GPU
# # when this build supports one and one is present, else the CPU.
# device: auto
#
# # What the model sees before each value: `none`, its `key`
# # (`name: Alice`), or its key `path` (`users.name: Alice`). Keys are
# # context only; they are never redacted.
# context: key
#
# # How sure the model must be of a finding, from 0 to 1.
# min_score: 0.5
#
# # Categories to report.
# categories:
# - account_number
# - private_address
# - private_date
# - private_email
# - private_person
# - private_phone
# - private_url
# - secret
#
# # Tokens read at once; longer documents are read in overlapping
# # windows. Memory grows with the square of this.
# max_tokens: 1024
#
# # How labels are decoded into spans: an operating point from the
# # model's viterbi_calibration.json, or the six transition biases
# # written out (transition_bias_background_stay, ...).
# viterbi: default
#
# # The model's architecture, when not the one in its config.json.
# # model: { ... }
## Exact strings, redacted wherever they appear, even mid-sentence. For
## secrets you already know the text of.
# - value:
# values:
# - hunter2
## Every value at a matching key path, redacted whatever it holds. A path is
## the keys of the enclosing objects joined with `.`, ending in the value's
## own key; array nesting adds nothing, so one entry covers every element. In
## these globs `*` stays inside one key, `**` spans any number, `?` is one
## character.
## users.ssn the ssn of every element of `users`
## **.customer_name a customer_name at any depth
## db.*.password the password of each immediate child of `db`
## secrets.** everything under `secrets`, and `secrets` itself
##
## A key listed under `policy.skip_keys` or `skip_key_suffixes` above is
## never scanned, so listing it here does nothing: take it out of `policy`
## first.
# - path:
# paths:
# - users.ssn
# ---------------------------------------------------------------------------
# allow — don't ever redact these values.
#
# Allowing is the last word: a value listed here stays in the document even
# when a detector above reported it.
# ---------------------------------------------------------------------------
allow:
# Exact values to leave in place, for confirmed false positives.
values:
# Patterns a secret must match IN FULL to be left in place.
regexes:
# Key paths never scanned at all. Unlike the two above, which spare a value
# after a detector reports it, these are never looked at, so they never
# become findings.
paths:
# ---------------------------------------------------------------------------
# agent — files AI coding agents must read redacted.
#
# Agents given the velociredactor skills read these files through
# `velociredactor redact` instead of directly, so the model never sees the
# secrets in them. With `enforce`, agents that support hooks are blocked from
# reading them any other way. `velociredactor agent init` writes this section;
# `velociredactor agent check FILE` tells whether a file is covered.
#
# Patterns are relative to this file and follow .gitignore conventions: a
# pattern with no `/` matches a file name in any directory, one with a `/`
# matches from this directory, and a trailing `/` covers a whole directory.
# `*` stays within one path segment and `**` spans any number.
# ---------------------------------------------------------------------------
# agent:
# protected:
# - .env*
# - "*.pem"
# - secrets/
# exclude:
# - .env.example
# enforce: false