Documentation
¶
Overview ¶
Package portcullis detects and redacts API tokens, cloud credentials, and other secret material in arbitrary text.
Contains reports whether any rule matches the input; Redact replaces every detected secret span with Marker while preserving the surrounding text. Both are safe for concurrent use and idempotent.
Performance ¶
Detection runs in O(len(text)) per rule via Go's RE2-based regexp engine, gated by an Aho–Corasick keyword pre-filter so clean inputs typically don't compile or run any regex. Memory allocations are zero on a clean input and small (a few hundred bytes) on a secret-bearing one.
Caller responsibilities ¶
portcullis intentionally does not cap input size: callers process inputs of widely different shapes (a chat message, a tool's stdout, a multi-megabyte log buffer) and can pick the right upper bound for their context. If the input is attacker-controlled and unbounded — e.g. an HTTP request body relayed through an untrusted intermediary — wrap the call site with the appropriate size limit before invoking Redact / Contains.
Provenance ¶
The default ruleset is derived from the MIT-licensed github.com/docker/mcp-gateway/pkg/secretsscan package, which adapted it from github.com/aquasecurity/trivy/pkg/fanal/secret, extended with additional patterns for modern AI providers, payment processors, and infrastructure tokens.
Index ¶
Examples ¶
Constants ¶
const Marker = "[REDACTED]"
Marker replaces every detected secret span. Chosen so it doesn't match any rule's keyword pre-filter — see TestMarkerIsNotASecret for the safety property that makes Redact idempotent.
Variables ¶
This section is empty.
Functions ¶
func Contains ¶
Contains reports whether text matches any built-in secret rule. It is safe for concurrent use.
Example ¶
package main
import (
"fmt"
"github.com/docker/portcullis"
)
func main() {
fmt.Println(portcullis.Contains("hello world"))
fmt.Println(portcullis.Contains("token=ghp_cxLeRrvbJfmYdUtr70xnNE3Q7Gvli43s19PD"))
}
Output: false true
func Redact ¶
Redact returns a copy of text with every detected secret span replaced by Marker. When a rule defines a (?P<secret>…) named subgroup, only that span is replaced (so callers still see "AWS_SECRET_ACCESS_KEY=[REDACTED]"); otherwise the whole match is replaced.
Idempotent: Marker does not match any rule, so calling Redact twice yields the same result. Safe for concurrent use.
Example ¶
package main
import (
"fmt"
"github.com/docker/portcullis"
)
func main() {
log := "Run this with token=ghp_cxLeRrvbJfmYdUtr70xnNE3Q7Gvli43s19PD please."
fmt.Println(portcullis.Redact(log))
}
Output: Run this with token=[REDACTED] please.
Example (ConnectionString) ¶
package main
import (
"fmt"
"github.com/docker/portcullis"
)
func main() {
// Connection-string rules redact only the password span so the
// surrounding URL stays useful for log readers.
uri := "postgresql://app:hunter2supersecret@db.internal:5432/orders"
fmt.Println(portcullis.Redact(uri))
}
Output: postgresql://app:[REDACTED]@db.internal:5432/orders
Example (MultipleSecrets) ¶
package main
import (
"fmt"
"github.com/docker/portcullis"
)
func main() {
in := "first ghp_cxLeRrvbJfmYdUtr70xnNE3Q7Gvli43s19PD " +
"and second ghp_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA end"
fmt.Println(portcullis.Redact(in))
}
Output: first [REDACTED] and second [REDACTED] end
Types ¶
This section is empty.