When the output looks right, wire it into CI so a regression fails the build. See CI integration.
Supported targets
A target is a technology StackGuard knows how to parse. One scan can cover your whole repository across all of them.
The nine targets
Target
Detects
compose
Docker Compose services, networks and volumes
dockerfile
Dockerfile instructions and build stages
kubernetes
Kubernetes manifests (the pod spec wherever it lives)
helm
Helm charts and rendered templates
terraform
Terraform resources, data sources and modules
github
GitHub Actions workflows and steps
jenkins
Jenkins pipeline files
ansible
Ansible plays and tasks
nginx
nginx server and location configuration
How detection works
Many targets share the .yaml extension, so the registry asks parsers in priority order: path-based first (GitHub workflows, Helm charts), then name-based, then content sniffing. Kubernetes looks for apiVersion and kind; Ansible looks for plays. Only the first 4 KiB of a file is read for detection.
Scanning a subset
Restrict a run to specific targets with --target, or set targets in the config file.
$ stackguard scan . --target kubernetes,terraform
Scanning & output
The scan command prints to the console by default. Switch formats for pipelines, dashboards or code scanning.
Severity gate: --fail-on error fails only on errors. Raise it to warning as the codebase improves.
Score gate: --min-score 80 fails when the production-readiness score drops below the threshold.
Use either or both. The first one to trip fails the build.
Other CI systems work the same way. Run the binary, check the exit code. SARIF and JSON output make findings portable to any dashboard.
Configuration
StackGuard reads .stackguard.yaml from the current directory upward, stopping at the repository root. Flags and environment variables override it.
The config file
# Rule IDs that never run.disabled_rules:
- COMP_SEC_003
# Off-by-default rule IDs to turn on.enabled_rules:
- COMP_PROD_005
# Replace the severity of specific rules.severity_overrides:
K8S_REL_003: WARNING
# Glob patterns to skip, relative to each scanned directory.exclude:
- "**/testdata/**"
- "*.generated.yaml"# Only scan these targets.targets: [compose, kubernetes, terraform]
# Extra directories of rule files, relative to this file.rules_dirs:
- ./policies
# Scan profile: quick, standard or strict.profile: standard
# Severity that fails the scan (or none), and the score gate.fail_on: ERROR
min_score: 80
format: console
Unknown keys are an error, so a typo like sevrity: fails loudly instead of being silently ignored. min_score must be between 0 and 100, and fail_on also accepts none or never to disable the severity gate.
Precedence
From lowest to highest: built-in defaults, the config file, environment variables, then command-line flags. Environment variables use the STACKGUARD_ prefix, for example STACKGUARD_MIN_SCORE, STACKGUARD_PROFILE or STACKGUARD_FAIL_ON. --disable and --exclude are added to the configured lists rather than replacing them.
Ignoring files
A .stackguardignore file holds one glob per line. These directories are always skipped: .git, .hg, .svn, node_modules, vendor, .terraform, .venv, venv, __pycache__, .idea and .stackguard. Files larger than 5 MiB are skipped too.
Writing custom rules
A rule is a YAML file. It names the resources it applies to and describes the bad condition as a query. No Go code is needed.
Anatomy of a rule
id: ACME_SEC_001 # TARGET_CATEGORY_NNNtitle: Service runs as root
severity: ERROR # ERROR | WARNING | INFOcategory: security # security | reliability | operational | maintainability | performancetarget: compose
message: "Service '{{ .Resource.Name }}' runs as root"remediation: |
Set a non-root `user:` in the service definition.
query:
not:
path: user
exists: true
Paths
Paths are evaluated against a resource's properties.
Syntax
Meaning
a.b.c
nested keys
ports.0.target
list index
ports.*.target
every element of a list or map
services.*.**
every scalar below the node
depends_on.{dep}
capture a map key into a variable
Operators and combinators
Leaf conditions use operators such as equals, exists, contains, in, matches (RE2), and numeric comparisons like gte. Combine them with all, any and not, or use where to require several conditions on the same element.
When a finding is reviewed and accepted, silence it in place with a comment instead of disabling the rule everywhere.
Inline suppressions
A comment containing stackguard:ignore suppresses findings on its own line and the line directly below it.
# stackguard:ignore COMP_SEC_001,COMP_SEC_003 -- reviewed in SEC-142
privileged: true
With no rule IDs listed, it suppresses every rule on that line. The number of suppressed findings is reported in stats.suppressed in the JSON output.
Disabling a rule entirely
To turn a rule off across the whole repository, add its ID to disabled_rules in the config file or pass --disable. See Configuration.
CLI reference
Everything runs through stackguard and five sub-commands.
scan
Scan files or directories for issues. Usage: stackguard scan [path...]
Flag
Description
-p, --profile
scan profile: quick, standard or strict (default standard)
-f, --format
console, json, sarif or html (default console)
--fail-on
severity that fails the scan: error, warning, info or none
--min-score
exit 1 if the score falls below this value
-o, --output
write the report to a file
--target
only scan these targets
--disable / --enable
turn rules off, or switch on off-by-default rules
--exclude
glob patterns to skip
--rules-dir
extra directory of rule files (repeatable)
-j, --jobs
parallel parsers (default: number of CPUs)
-q, --quiet
print only findings and the score
--show-passed
console: list every passed rule instead of a summary
--omit-skipped
json/sarif: drop rules with status skipped
score
Print the production-readiness score only. Usage: stackguard score [path...]. Flags: -f, --format (console or json), --min-score, -p, --profile, plus the shared filters --target, --exclude, --rules-dir and -j, --jobs.
rules
List the available rules, or validate a rule directory. Usage: stackguard rules. Flags: --target, --category, --severity, -f, --format (table or json), --rules-dir and --validate.
explain
Show the details, reason and remediation for one rule. Usage: stackguard explain RULE_ID. Flags: -f, --format (text or json) and --rules-dir. It also shows which file the rule loaded from.
version
Print the StackGuard version, commit, build date and platform. Usage: stackguard version. Flag: -f, --format (text or json).
Global flags
Available on every command: --config, --log-level, --log-format, -v, --verbose and --no-color. The NO_COLOR environment variable is also honored.
Rules
Rules ship embedded in the binary and merge with any local layers you add. Each one has a stable ID and a category.
Rule IDs
IDs follow TARGET_CATEGORY_NNN, for example TF_SEC_001 or K8S_OPS_001. The ID is stable, so you can reference it in suppressions and config.
Categories
Category
Covers
security
Exposure, privilege and access problems
reliability
Availability, data safety and resilience
operational
Limits, health checks and day-two operations
maintainability
Reproducibility and drift, such as unpinned images
performance
Resource use and efficiency
Layered loading
Rules load in layers and later layers win on a shared ID: built-in rules, then $STACKGUARD_HOME/rules, then .stackguard/rules, then rules_dirs from config, then --rules-dir flags.
Off by default
Some rules assume an organisation-specific convention and ship off by default. Turn them on with enabled_rules or --enable. Run stackguard rules to see which are marked.
Exit codes & scoring
The score turns a list of findings into one number, and the exit code turns that into a pass or fail for CI.
The score
Every scan starts at 100 and loses points per finding, down to a floor of 0.
Severity
Deduction
ERROR
-10 points
WARNING
-3 points
INFO
-1 point
So one error, two warnings and one info gives a score of 83.
Gating on the score
--min-score, or min_score in config, fails the run when the score is below the threshold. A value of 0 disables the check.
Exit codes
Code
Meaning
0
Success. No finding reached the fail-on severity and the score passed.
1
A finding reached the fail-on severity, or the score fell below min-score.
2
Usage error (bad flags, arguments, configuration or rules).
3
Internal error (an unexpected I/O or runtime failure).