Security
Redaction
The rule categories that replace secrets with [REDACTED], where each rule is applied across collectors, the two providers, how to extend the rules, and why the off switch is a bad idea.
Secronyx redacts credentials inside the collector before a result leaves the process. Redaction is on by default with the built-in provider. Most collector outputs that can carry a secret pass through one of a small set of helpers in internal/redact; a few drop the secret-bearing field structurally instead. This page describes the rules, where they run, and how to change them. The path policy, not redaction, decides which files are reachable at all; see Network and path policy.
Defaults and flags
| Flag | Default | Effect |
|---|---|---|
--redact |
true |
Enable output redaction. |
--no-redact |
false |
Disable it. The server logs WARNING: output redaction is DISABLED; secrets in system state will be returned verbatim. |
--redact-provider |
default |
Provider name: default or gitguardian. |
The compiled-in configuration is Enabled: true, ProviderName: "default". Two behaviours of provider selection matter operationally. A provider name that is not registered falls back silently to default (Configure looks the name up and substitutes the default provider when nothing is found). And a provider named noop is registered for internal use; selecting it leaves IsEnabled() true but makes RedactValue, RedactMap and the value-pattern checks pass everything through. The free-text rules and the fixed command-line option list below are provider-independent and still apply, so noop is neither "off" nor a coherent way to "turn redaction down".
Every redacted value is replaced by the constant RedactedPlaceholder, which is [REDACTED]. There is no reversible form; the original bytes are gone from the response.
Rule categories
Redaction combines four kinds of rule. The first two are the provider's; the command-line and free-text helpers are package-level, and RedactLine uses its own fixed patterns regardless of provider. Each helper below returns its input unchanged when redaction is disabled.
Field-name rules
RedactValue(fieldName, value) replaces the whole value when the field or variable name contains one of these keywords, case-insensitively, anywhere in the name:
password passwd pwd pass passphrase
secret
api_key apikey token access_token refresh_token id_token auth_token
private_key privatekey ssl_key sslkey keystore truststore
auth authorization credential credentials
So the value of DB_PASSWORD becomes [REDACTED], and HOSTNAME=web-01 is untouched. RedactMap applies RedactValue to every entry of a map[string]string, keyed on that entry's own name.
Value-pattern rules
RedactValue also replaces a value regardless of its name when IsSensitiveValue matches it. A value qualifies when it is at least 8 characters and matches one of the compiled patterns:
- a URL with userinfo,
scheme://user:pass@host - an AWS access key id:
AKIA,AIDA,AROA,AIPA,ANPA,ANVAorASIAfollowed by 12 to 17 upper-case alphanumerics - a 40-character base64-like string (the AWS secret key shape)
- a JWT: three base64url segments, the first two beginning
eyJ - a PEM private key marker:
-----BEGIN (RSA|DSA|EC|OPENSSH|ENCRYPTED|PRIVATE) ... KEY----- - GitHub tokens
ghp_,gho_,ghu_,ghs_,ghr_followed by 36 or more characters - Slack tokens
xox[baprs]-... - Stripe keys
sk_live_andrk_live_followed by 24 or more characters Bearer <token>as a whole value- 32 or more hex characters, and separately exactly 64 hex characters
The hex and 40-character base64 rules are deliberately aggressive: a 40-character git commit hash stored as a standalone field value is replaced. This is a known false positive and is accepted because a field value is a poor place to distinguish a hash from a key.
Command-line rules
RedactCommandLine handles the three shapes in which credentials appear on a command line:
mysqldump --user=app --password=hunter2 appdb → mysqldump --user=app --password=[REDACTED] appdb
--password hunter2 → --password [REDACTED]
AWS_SECRET_ACCESS_KEY=... aws s3 ls → AWS_SECRET_ACCESS_KEY=[REDACTED] aws s3 ls
An option name matches when, after stripping leading - and / characters, it is one of:
password passwd pass pwd secret token apikey api-key api_key
access-key access_key secret-key secret_key client-secret client_secret
private-key private_key auth authorization credential credentials
bearer session cookie otp passphrase pin
An upper-case environment-style key (FOO_PASSWORD=) reuses the field-name rules. Any standalone argument that matches a value pattern (a bearer token, an AWS key, a PEM blob) is also replaced.
Free-text rules
RedactLine runs over a whole line (a log message, an evidence string, a configuration value) and replaces only the secret part so the line stays useful:
key=valueorkey: valuepairs whose key ispassword,passwd,passphrase,pwd,pass,secret,token,api_key,access_key,secret_key,client_secret,private_key,credential(s),authorization,auth_tokenorbearer(with-or_separators)- userinfo inside a URL:
postgres://app:s3cret@dbbecomespostgres://app:[REDACTED]@db Bearer <token>andBasic <credentials>schemes- JWT, AWS key id, GitHub, Slack, Stripe, GitLab (
glpat-) and Google (AIza) token formats - any line containing a PEM private key marker is replaced entirely with
[REDACTED] (private key)
RedactLine deliberately does not apply the long-hex or 40-character base64 rules. Inside a log line those would erase commit hashes, request ids and checksums that are the point of the line:
commit 9fceb02d0ae598e95dc970b74767f19372d61af8 deployed → unchanged
request abc123 failed: Authorization: Bearer eyJ... → request abc123 failed: Authorization: Bearer [REDACTED]
RedactConnectionString keeps the scheme, user and host and replaces the password: mysql://root:pw@localhost:3306/db becomes mysql://root:[REDACTED]@localhost:3306/db. It is the only helper that is specific to DefaultProvider: with any other provider it falls back to RedactValue("connection_string", ...), where the value-pattern rule replaces the whole string rather than just the password.
Where redaction is applied
The table lists every collector output that can carry a credential and the treatment it receives. "Structural" means the collector never emits the secret-bearing field at all.
| Tool or collector | Field | Treatment |
|---|---|---|
get_processes, get_processes_sampled (Linux) |
cmdline |
RedactCommandLine |
get_process_environ |
environ values |
RedactMap; the number of replaced values is reported in redacted_count |
get_env_vars |
process environment and /etc/environment values |
RedactValue per variable |
get_journal_logs, get_syslog, get_kernel_logs, get_auth_logs, get_app_logs, get_event_log |
entries[].message, entries[].fields |
RedactLine on messages; RedactMap then RedactLine on structured fields; applied in the exported wrapper on every platform |
get_scheduled_tasks, get_cron_jobs, get_startup_items |
command |
RedactCommandLine |
get_registry_key, get_registry_tree |
values[].data |
see the registry rules below |
Windows UAC audit (secaudit) |
registry query values | RedactValue then RedactLine |
nginx sites and upstreams (websrv) |
proxy_pass_targets[], servers[] |
RedactLine (URL userinfo) |
get_fstab_entries |
entries[].options |
RedactLine (password=, credentials= in CIFS and NFS options) |
Forensic snapshot and auth, sudo, install and system events (compliance) |
processes[].cmdline, events[].details |
RedactCommandLine, RedactLine |
Go and Node environment (runtimeinfo) |
goflags, node_options; goproxy, goprivate |
RedactLine; URL userinfo stripped structurally |
PostgreSQL, MySQL, Redis, MongoDB, Elasticsearch settings (dbsrv) |
configuration keys | Structural: any key matching pass, auth, secret, key, credential, token or user is dropped before output |
Git configuration (devtools) |
credential_helper |
Structural: reduced to the helper name; stored credentials are never read |
get_app_config |
file content | its own line-oriented rules, path-policy gated, summarised in redaction_summary |
Registry values
redactRegistryValue in internal/windows/registry_redact.go applies rules in this order:
- Under a sensitive key every string is replaced, binary data is reduced to
[REDACTED] (n bytes), and for the SNMPValidCommunitieskey the value name itself is replaced, because there the community string is the name. Sensitive key fragments are\policy\secrets,\lsa\,\snmp\parameters\validcommunities,\odbc\odbc.ini,\putty\sessions,\realvnc\,\tightvnc\,\ultravnc\,\credentials,\vault\,\dpapi,\cryptography\and\keys\. - Elsewhere a string value is passed through
RedactValue(soDefaultPasswordandProxyPasswordare replaced by name) and thenRedactLine(sokey=valuecredentials inside longer strings such as a serviceImagePathare scrubbed). - Binary data under a name that matches the field rules is reduced to its length; other binary and numeric data passes through.
Configuration files
get_app_config uses its own line-oriented rules because it must keep the file readable. A line whose key matches password, passwd, pwd, secret, private, token, apikey, api_key, api-key, credential, cred, auth, authentication, certificate, cert, connection_string, connectionstring, connstr, access_key, secret_key or private_key has its value replaced, unless the value is an environment variable reference (${VAR}, $VAR, %VAR%), which is kept. Lines that survive the key check are scanned for connection strings, AWS keys, JWTs, bearer tokens, PEM blocks, Azure AccountKey= values, and 32-character or longer alphanumeric or hex strings; a match that is itself an environment variable reference or a template ({{ ... }}) is left alone. Environment variable and template references are counted across the whole file either way. The result carries a redaction_summary with total_redactions, by_type, env_var_refs, template_refs and redacted_keys.
Providers
default
Built-in pattern matching, offline, no dependencies. This is the provider unless you choose otherwise and is what every rule above describes.
gitguardian
GitGuardianProvider uses the same field-name keywords and selects a value-detection mode at each call:
- API mode when an API key is available from the
APIKeyfield or theGITGUARDIAN_API_KEYenvironment variable:POST https://api.gitguardian.com/v1/scanwith the value asdocument, authorised withAuthorization: Token <key>; a non-zeropolicy_break_countmeans sensitive. - CLI mode when
ggshieldis onPATH: the value is written to a temporary file and scanned withggshield secret scan path <file> --json; exit code 1 or a non-zerototal_incidentsmeans sensitive. - Pattern mode otherwise, using a built-in list modelled on GitGuardian's public detectors (AWS, GitHub including
github_pat_, GitLab, Slack, Stripe, Google, Twilio, SendGrid, Mailchimp, npm, PyPI, Shopify, Heroku, DigitalOcean, Discord, HashiCorp Vault, Azure storage keys, database URLs, private keys and certificates, JWTs, and a generickey=valuerule for 16-character or longer values).
Calls time out after 5 seconds and results are cached per value (1,000 entries, half evicted when full). GetDetectionMode() reports which mode is in effect.
Consider what API mode means before enabling it: every candidate value that reaches IsSensitiveValue is sent off-host to GitGuardian. That includes values that turn out not to be secrets. Pattern mode and CLI mode stay on the host.
# GitGuardian with the API
GITGUARDIAN_API_KEY=... secronyx --redact-provider gitguardian
# GitGuardian with ggshield installed, no API key
secronyx --redact-provider gitguardianAdding patterns
There is no command-line flag for extra keywords or patterns. The default provider does accept them programmatically:
p := redact.NewDefaultProviderWithOptions(
[]string{"licence_key", "webhook"}, // extra field-name keywords
[]string{`^acme_[A-Za-z0-9]{32}$`}, // extra value patterns (Go regexp)
)
_ = redact.Configure(redact.Config{Enabled: true, Provider: p})WithKeywords and WithPatterns do the same on an existing provider. A pattern that fails to compile is skipped rather than reported. Alternatively implement the Provider interface and register it:
type Provider interface {
Name() string
IsSensitiveField(fieldName string) bool
IsSensitiveValue(value string) bool
RedactValue(fieldName, value string) string
RedactMap(m map[string]string) map[string]string
}func init() { redact.RegisterProvider(&MyProvider{}) }
// then: secronyx --redact-provider myproviderThe command-line helper RedactCommandLine uses the active provider's field and value classification on top of its own fixed option-name list, so a custom provider gets the same command-line treatment. RedactLine, by contrast, uses its own fixed pattern set regardless of provider.
Because internal/redact is an internal Go package, either approach means building the binary from a tree that contains your provider. See Contributing.
Disabling redaction, and why not to
--no-redact switches every helper to pass-through in one step: command lines, process environments, logs, registry values, configuration files and the rest are returned exactly as read. There is no per-collector switch. The reasons to leave it on:
- The client is an AI agent that will quote what it reads into its context, and from there into transcripts, tickets and summaries that have none of the host's access controls.
- The audit log records the tool name, its parameters and an outcome (
success,errorordenied), never the result payload, so a secret that leaves via a result leaves no trace of having done so. See Audit logging. - The false positives are cheap. A redacted commit hash in a process argument costs one follow-up question; a leaked deployment key costs a rotation.
If a specific value is being wrongly redacted, narrow the query or read the value with a tool that reports it structurally (for example, get_processes for a process name rather than its full command line) before reaching for the flag.
Known limits
- Detection is pattern based. A password that is a plain word, presented without a
password=style label, is not recognised. - Redaction runs in the collector process. With
--no-redactnothing downstream restores it. - The default provider's standalone-value rules are aggressive by design; the free-text rules are conservative by design. A secret that appears only as an unlabelled token in prose and does not match a known token format survives.
- Field-name rules match substrings, so a variable named
PASSPORT_NUMBERis redacted (it containspass). This is accepted.
For the tests that back the table above, see docs/security/redaction-coverage.md in the repository, which names the test behind each entry in its inventory.
Built 2026-09-19. Source: levantar-ai/secronyx. Found a mistake? Tell us.