CTGuard uses SSA-based taint tracking to find code paths where secret data leaks through execution timing. It catches comparisons with ==, branches on private keys, secret-dependent indexing, and more.
macOS / Linux
brew install oasilturk/tap/ctguardor
go install github.com/oasilturk/ctguard/cmd/ctguard@latestWindows
go install github.com/oasilturk/ctguard/cmd/ctguard@latestPre-built binaries for all platforms are available on the Releases page.
Mark secret parameters, then scan:
//ctguard:secret key
func Verify(key []byte, message []byte) bool {
return bytes.Equal(key, expected) // CTGuard flags this
}ctguard ./...| Rule | Description | Example |
|---|---|---|
| CT001 | Secret-dependent branching | if secret == "admin" |
| CT002 | Non-constant-time comparison | bytes.Equal(secret, input) |
| CT003 | Secret-dependent indexing | table[secret[i]] (cache timing) |
| CT004 | Secret exposure in logs/errors | log.Printf("%s", secret) |
| CT005 | Variable-time arithmetic | secret / n, secret % n |
| CT006 | Secret on channels | ch <- secret |
| CT007 | Secret in I/O sinks | conn.Write(secret) in isolated regions |
//ctguard:secret key
func Check(key string) {
normalized := strings.ToLower(key)
if normalized == "admin" { // CT001: branch depends on secret
grantAccess()
}
}auth.go:4:5 CT001: branch depends on secret 'key' (confidence: high)
Fix with constant-time operations:
//ctguard:secret key
func Check(key string) {
normalized := strings.ToLower(key)
if subtle.ConstantTimeCompare([]byte(normalized), []byte("admin")) == 1 {
grantAccess()
}
}No issues found
ctguard ./... # Plain text (default)
ctguard -format=json ./... # JSON
ctguard -format=sarif ./... # SARIF (GitHub Code Scanning)- uses: oasilturk/ctguard@main- uses: oasilturk/ctguard@main
with:
format: sarif
args: "-fail=false ./..."
sarif-file: ctguard.sarif
- uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: ctguard.sarifCreate .ctguard.yaml in your project root:
rules:
enable: [all]
disable: [CT003]
exclude:
- "vendor/**"
- "**/*_test.go"All options
annotations:
secrets:
- package: "github.com/vendor/pkg"
function: "Compare"
params: ["secret"]
ignores:
- package: "github.com/vendor/pkg"
function: "SafeFunc"
rules: all
format: json
fail: true
summary: true
min-confidence: lowSee .ctguard.yaml.example for a full reference.
//ctguard:ignore CT002 -- constant prefix check, not a timing risk
return strings.HasPrefix(token, "Bearer ")//ctguard:ignore // all rules
//ctguard:ignore CT001 // specific rule
//ctguard:ignore CT001 CT002 // multiple rulesCTGuard integrates with go vet as a custom analyzer. It builds an SSA representation of your code, then:
- Collects
//ctguard:secretannotations to identify sensitive parameters - Performs interprocedural taint tracking (fixed-point iteration across function boundaries)
- Runs 7 specialized rule checkers against the taint graph
- Reports findings with confidence levels (high/low) based on taint precision
See CONTRIBUTING.md for guidelines and SECURITY.md for reporting vulnerabilities.
MIT © oasilturk
