100% free and open source · every tool, every host, no licence key · read the licence
Secronyx

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, ANVA or ASIA followed 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_ and rk_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=value or key: value pairs whose key is password, passwd, passphrase, pwd, pass, secret, token, api_key, access_key, secret_key, client_secret, private_key, credential(s), authorization, auth_token or bearer (with - or _ separators)
  • userinfo inside a URL: postgres://app:s3cret@db becomes postgres://app:[REDACTED]@db
  • Bearer <token> and Basic <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:

  1. Under a sensitive key every string is replaced, binary data is reduced to [REDACTED] (n bytes), and for the SNMP ValidCommunities key 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\.
  2. Elsewhere a string value is passed through RedactValue (so DefaultPassword and ProxyPassword are replaced by name) and then RedactLine (so key=value credentials inside longer strings such as a service ImagePath are scrubbed).
  3. 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:

  1. API mode when an API key is available from the APIKey field or the GITGUARDIAN_API_KEY environment variable: POST https://api.gitguardian.com/v1/scan with the value as document, authorised with Authorization: Token <key>; a non-zero policy_break_count means sensitive.
  2. CLI mode when ggshield is on PATH: the value is written to a temporary file and scanned with ggshield secret scan path <file> --json; exit code 1 or a non-zero total_incidents means sensitive.
  3. 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 generic key=value rule 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 gitguardian

Adding 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 myprovider

The 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, error or denied), 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-redact nothing 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_NUMBER is redacted (it contains pass). 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.