Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ This file is for agents changing the gatecheck repository. To use the gatecheck
## Conventions

- Check `gatecheck <command> --help` before describing a flag. `docs/cli-reference.md` matches the CLI on main.
- Report type detection is a lowercase substring of the filename. See `docs/supported-reports.md`.
- Report type is detected from file contents. A lowercase filename substring is the fallback when the contents are ambiguous. See `docs/supported-reports.md`.
- Coverage thresholds and EPSS scores are ratios from 0 to 1.
- Exit codes from `cmd/gatecheck`: 0 success, 1 threshold failure (`gatecheck.ErrValidationFailure`), 255 any other error (`os.Exit(-1)`). `grype`, `syft`, `gitleaks`, `semgrep`, `osemgrep`, `trivy`, and `osv-scanner` return the scanner's status. A missing scanner still exits 255.
- Do not add copyright notices.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- `list` and `validate` read SARIF 2.1.0 from any tool, including KICS, Checkov, Bandit, govulncheck, and `semgrep --sarif`. Severity limits use `security-severity` when it is set, and the SARIF level otherwise. `sarif.ruleLimit` denies rule ids and `sarif.ruleRiskAcceptance` allows them. A SARIF report with no findings prints `No sarif findings`.
- `gatecheck bootstrap` installs pinned Trivy 0.76.1, and `gatecheck trivy` runs it.
- Trivy JSON reports work with `list`, `validate`, and `bundle`. Severity limits and CVE allow and deny lists follow the Grype rules.
- `gatecheck bootstrap` installs pinned osv-scanner 2.7.0, and `gatecheck osv-scanner` runs it.
- osv-scanner JSON reports work with `list`, `validate`, and `bundle`. Severity limits and CVE allow and deny lists follow the Grype rules.

### Changed

- Report type comes from the file contents. The filename is used only when the contents are ambiguous.
- Any CycloneDX JSON BOM with vulnerabilities is listed and validated with the `cyclonedx` rules, whichever tool wrote it. A missing severity word falls back to the CVSS score.
- `gatecheck bootstrap --list` widens the tool column so `osv-scanner` fits.

### Fixed

- A CycloneDX file with `grype` in the name was read as Grype, so its vulnerabilities were ignored. Fixes #80.

## [0.9.0] - 2026-10-23

### Added
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Generate a configuration file with the default thresholds. Every rule starts dis
gatecheck config init > gatecheck.yaml
```

Validate a report. The config is a flag. The report type comes from the filename, so a Grype report needs `grype` in the name.
Validate a report. The config is a flag. The report type comes from the file contents. The filename is used when the contents are ambiguous, so a Grype report can still be named `grype-report.json`.

```shell
gatecheck validate --config gatecheck.yaml grype-report.json
Expand Down Expand Up @@ -66,7 +66,7 @@ gatecheck validate --help

### Bundling Artifacts

Bundling artifacts produces a gzipped tarball and a manifest. The bundle filename needs to contain `bundle` so `list` and `validate` treat it as a bundle.
Bundling artifacts produces a gzipped tarball and a manifest. `list` and `validate` recognize a bundle from that manifest. A name that contains `bundle` still works when the contents are ambiguous.

```shell
gatecheck bundle create gatecheck-bundle.tar.gz grype-report.json
Expand Down
44 changes: 24 additions & 20 deletions cmd/list.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
package cmd

import (
"bytes"
"errors"
"fmt"
"io"
"log/slog"
"os"
"slices"
"strings"

"github.com/gatecheckdev/gatecheck/pkg/gatecheck"
"github.com/spf13/cobra"
)

var supportedTypes = []string{"grype", "semgrep", "gitleaks", "syft", "cyclonedx", "bundle", "gatecheck", "trivy", "osv"}

var listCmd = &cobra.Command{
Use: "list",
Short: "print a table of the findings in a report or files in a gatecheck bundle",
Expand Down Expand Up @@ -97,17 +95,29 @@ var listAllCmd = &cobra.Command{
slog.Debug("run list all", "epss", fmt.Sprintf("%v", epss), "markdown", fmt.Sprintf("%v", markdown))

for _, filename := range args {
supportedFunc := func(s string) bool {
return strings.Contains(filename, s)
}
cmd.Printf("%s\n", filename)
if !slices.ContainsFunc(supportedTypes, supportedFunc) {
slog.Warn("file not supported, skip", "filename", filename)

src, err := os.Open(filename)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
slog.Error("file not found, skip", "filename", filename)
continue
}
slog.Error("cannot open file, skip", "filename", filename, "error", err)
continue
}
content, err := io.ReadAll(src)
if closeErr := src.Close(); closeErr != nil {
slog.Debug("close file", "filename", filename, "error", closeErr)
}
if err != nil {
slog.Error("cannot read file, skip", "filename", filename, "error", err)
continue
}

if _, err := os.Stat(filename); errors.Is(err, os.ErrNotExist) {
slog.Error("file not found, skip", "filename", filename)
supported, withEPSS := gatecheck.ListAllAction(content, filename)
if !supported {
slog.Warn("file not supported, skip", "filename", filename)
continue
}

Expand All @@ -121,7 +131,7 @@ var listAllCmd = &cobra.Command{
}
opts = append(opts, displayOpt)

if epss && slices.ContainsFunc([]string{"grype", "cyclonedx", "trivy", "osv"}, supportedFunc) {
if epss && withEPSS {
epssOpt, err := gatecheck.WithEPSS(epssFile, epssURL, epssCacheOptions()...)
if err != nil {
slog.Error("epss fetch failure, skip", "filename", filename, "error", err)
Expand All @@ -131,13 +141,7 @@ var listAllCmd = &cobra.Command{
}

dst := cmd.OutOrStdout()
src, err := os.Open(filename)
if err != nil {
slog.Error("cannot open file, skip", "filename", filename, "error", err)
continue
}

err = gatecheck.List(dst, src, filename, opts...)
err = gatecheck.List(dst, bytes.NewReader(content), filename, opts...)
if err != nil {
slog.Error("cannot list report, skip", "filename", filename, "error", err)
continue
Expand All @@ -157,7 +161,7 @@ func newListAllCommand() *cobra.Command {
}

func newListCommand() *cobra.Command {
listCmd.Flags().StringP("input-type", "i", "", "the input filetype if using STDIN [grype|semgrep|gitleaks|syft|bundle|trivy|osv]")
listCmd.Flags().StringP("input-type", "i", "", "stdin type word used when the report content is ambiguous [grype|cyclonedx|semgrep|gitleaks|trivy|osv|syft|sarif|bundle|lcov]")
listCmd.Flags().Bool("markdown", false, "print as a markdown table")
listCmd.Flags().Bool("epss", false, "List with EPSS data")
RuntimeConfig.EPSSURL.SetupCobra(listCmd)
Expand Down
Loading
Loading