CTGuard

Catch timing side-channel vulnerabilities in your Go code.
CTGuard finds vulnerabilities in code where secret data can be leaked through execution time, like when you compare passwords with == or branch on private keys. Each finding includes a confidence level to help you focus on the most certain issues.

What It Catches
| Rule |
What it detects |
| CT001 |
Branches and loops that depend on secret data (if secretKey == ...) |
| CT002 |
Non-constant-time comparisons (bytes.Equal on secrets) |
| CT003 |
Array/map indexing with secret indices (cache timing) |
| CT004 |
Secrets leaked to logs or error messages |
| CT005 |
Variable-time arithmetic operations (/, %, <<, >> on secrets) |
| CT006 |
Secret related channel operations (send/receive) |
| CT007 |
Secret data flowing into I/O sinks (network, file, syscall) within "isolated" regions |
Quick Example
Vulnerable Code:
//ctguard:secret key
func Check(key string) {
normalized := strings.ToLower(key) // taint propagates
if normalized == "admin" { // CT001: branch depends on secret!
grantAccess()
}
}
auth.go:4:5 CT001: branch depends on secret 'key' (confidence: high)
Fixed:
//ctguard:secret key
func Check(key string) {
normalized := strings.ToLower(key)
if subtle.ConstantTimeCompare([]byte(normalized), []byte("admin")) == 1 {
grantAccess()
}
}
✓ No issues found
Install
go install github.com/oasilturk/ctguard/cmd/ctguard@latest
Usage
Mark your secret parameters:
//ctguard:secret key
func Verify(key []byte, message []byte) bool {
return bytes.Equal(key, expected) // CTGuard will flag this
}
Run it:
ctguard ./...
Output formats:
ctguard ./... # Plain text (default)
ctguard -format=json ./... # JSON
ctguard -format=sarif ./... # SARIF (for GitHub Code Scanning)
Configuration
Create .ctguard.yaml in your project:
rules:
enable: [all]
disable: [CT003] # optionally disable rules
exclude:
- "vendor/**"
- "**/*_test.go"
Advanced Configuration
# Without modifying the code. Wildcards are supported.
annotations:
secrets:
- package: "github.com/vendor/examples"
function: "NonConstantTimeFunction"
params: ["secret"]
ignores:
- package: "github.com/vendor/examples"
function: "SafeFunction"
rules: all # or specific rules like ["CT001", "CT002"]
format: json # plain, json, or sarif
fail: true # exit code on findings
summary: true # show stats
min-confidence: low # low or high
See .ctguard.yaml.example for all options.
Tip: Use -min-confidence=high to filter out uncertain findings, or set min-confidence: high in config.
CI Integration
GitHub Actions (recommended):
- uses: oasilturk/ctguard@main
With options:
- uses: oasilturk/ctguard@main
with:
format: json
args: "-fail=false ./..."
With GitHub Code Scanning:
- 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.sarif
Manual installation
- run: go install github.com/oasilturk/ctguard/cmd/ctguard@latest
- run: ctguard ./...
Suppressing Findings
When you have a legitimate reason to ignore a finding:
//ctguard:secret token
func ParseToken(token string) bool {
//ctguard:ignore CT002 -- comparing constant prefix for parsing
return strings.HasPrefix(token, "Bearer ")
}
Learn More
License
MIT © oasilturk