Skip to content

git-pkgs/capcheck

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

capcheck

Fail CI when your Go code or its dependencies gain access to new privileged operations: spawning processes, opening sockets, calling cgo, touching the filesystem. Built on google/capslock.

govulncheck tells you when a dependency has a CVE. capcheck tells you when a dependency (or your own code) gains the ability to do something it couldn't before: spawn a process, open a socket, write a file, call cgo. You check a baseline into your repo and CI fails when the capability set drifts.

Install

go install github.com/git-pkgs/capcheck/cmd/capcheck@latest

Binaries for linux, darwin, and windows are attached to each release.

Quick start

From the root of a Go module:

capcheck init ./...

This analyses your packages, writes a default capcheck.json, and writes the current capability set to capcheck.lock.json. Commit both.

Thereafter, in CI or locally:

capcheck ./...

Exit code 0 means no new capabilities. Exit code 1 means something changed and you'll see the offending call path:

capcheck: 1 new capability since baseline

  github.com/you/app gained EXEC
    cmd/server/main.go:42:3   main.run
    handler/upload.go:88:5    handler.processImage
                              github.com/disintegration/imaging.Resize
                              os/exec.Command

Run `capcheck update` to accept, or remove the offending call path.

If the change is expected, run capcheck update ./... and commit the new lock file.

Commands

capcheck init   [packages]   analyse and write capcheck.json + capcheck.lock.json
capcheck check  [packages]   compare against the lock file (default command)
capcheck update [packages]   re-analyse and overwrite the lock file
capcheck list   [packages]   print current capabilities, no baseline involved

packages defaults to ./....

Flags accepted by all commands:

-c, --config string       path to capcheck.json (default "capcheck.json")
    --baseline string     path to capcheck.lock.json (overrides config)
-C, --dir string          run as if in this directory
-f, --format string       text, json, or github (default "text")
    --granularity string  package or function (overrides config)
    --timeout duration    analysis timeout (overrides config)
    --strict              fail on removed capabilities as well as added
    --omit-paths          do not record call paths (smaller lock file)
    --ignore strings      capabilities to ignore (repeatable, stacks with config)

Configuration

capcheck.json is optional; missing keys take defaults.

{
  "granularity": "package",
  "timeout": "5m",
  "baseline": "capcheck.lock.json",
  "ignore": [
    "FILES",
    "NETWORK",
    "REFLECT",
    "RUNTIME"
  ]
}

ignore matches hierarchically: an entry of MODIFY_SYSTEM_STATE also ignores MODIFY_SYSTEM_STATE/ENV. Most projects will want to ignore the noisy capabilities and watch only for EXEC, CGO, ARBITRARY_EXECUTION, UNSAFE_POINTER, and MODIFY_SYSTEM_STATE.

The full capability list is documented in capslock's docs.

granularity controls what counts as a change. At package (the default) you're told when a package gains a capability it didn't have. At function you're told when a new entry point reaches an existing capability, which is more precise but produces a much larger and more volatile lock file.

goos and goarch pin the platform the analysis runs as. capslock's results depend on which stdlib files are compiled in, so a lock file generated on macOS will not match one generated on Linux. init writes linux/amd64 by default since that is what most CI runs on; change it if your CI runs elsewhere, but pick one and stick with it.

Other keys: build_tags is passed through to package loading; capability_map points to a custom capslock classifier file if you want to extend the stdlib mappings; omit_paths drops the example call path from each lock entry, which shrinks capcheck.lock.json considerably at the cost of less helpful failure output.

GitHub Action

- uses: git-pkgs/capcheck@v1
  with:
    packages: ./...

The action installs capcheck, runs check --format github, and turns each new capability into an error annotation anchored at the first line of user code in the call path. On failure it also writes the human-readable diff to the job summary.

Inputs: go-version (default stable), capcheck-version (default latest), working-directory, packages, config, baseline, strict, ignore.

If you need to check multiple platforms, keep one config per platform and run the action in a matrix:

strategy:
  matrix:
    goos: [linux, darwin, windows]
steps:
  - uses: git-pkgs/capcheck@v1
    with:
      config: capcheck.${{ matrix.goos }}.json

where each capcheck.<goos>.json sets its own goos and baseline.

Exit codes

0  no new capabilities (or only removed ones, when not --strict)
1  capability set changed
2  analysis or configuration error

How it relates to capslock

capcheck does not reimplement any analysis. It calls github.com/google/capslock/analyzer directly and the lock file is the same protojson that capslock -output=json produces, so you can inspect or regenerate it with the upstream tool. What capcheck adds is the config file, the diff, the exit codes, and the CI ergonomics.

License

MIT

About

Fail CI when Go code or dependencies gain new privileged operations, using google/capslock

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Contributors

Languages