📎 Unreleased docs. This is the next version (from main), features here may change or not be in a release yet.See the latest release →

Scanners

gosec

On this page
  • Control: sast
  • Tool: gosec, https://github.com/securego/gosec
  • Status: ✅ implemented
  • Target: source repository (RepositoryTarget), checked out at the scanned revision
  • License / terms: Apache-2.0 (permissive). Exec only, do not bundle or import.

What it does

A Go-specialized static analyzer that complements the polyglot Semgrep with deeper Go-specific rules (AST/SSA). Checks out the component's repository and runs gosec -fmt sarif -no-fail -track-suppressions -concurrency 2 <module>/... once per Go module, for every directory holding a go.mod outside vendor/ and testdata/. A module below the root and a module nested inside another are each analyzed, and each finding is located under its own module's directory. A tree with no go.mod is reported as analyzed by nothing, never as clean.

Each module's run stands on its own:

  • A module with no Go package, such as a documentation site built with Hugo that has its own go.mod, makes gosec exit 1 with No packages found. Its go.mod is an unread-files caveat with the reason no Go packages, and the findings from the other modules are kept. The control is not in error because of it.
  • A module whose run fails for any other reason puts the control in error and the run is incomplete, with the module named in the error, as run gosec over tools/go.mod: exit status 1: …. The findings from the modules gosec did read are kept in the report beside the error.

A component scoped by paths to part of a module shares an analysis of the whole module, and keeps only the findings in its own files. Scoping a repository has the rule for shared code no component names.

  • -no-fail keeps the process successful when findings exist (findings live in the SARIF report, not the exit code; the sast controller judges severity).
  • -track-suppressions keeps a #nosec result in the report, marked, carrying the text after the -- as its justification. Without it gosec removes the result, and a finding somebody excluded reads as a finding nobody ever made.
  • -quiet is deliberately not used: it suppresses all output on a clean scan, which would leave no SARIF to parse.
  • -concurrency 2 loads two packages at once, each building with -p at half of the parallelism bound n, rounded down, so the compile processes of both loads together stay within n. Where n is 1, gosec loads one package at a time. A -p already in your GOFLAGS is kept.

Severity

gosec rates each rule HIGH, MEDIUM or LOW, and a finding's severity is that rating: high, medium or low. gosec writes SARIF level error for HIGH and MEDIUM rules alike and records the rating as a tag on the rule, so Draugr reads the tag rather than the level. A rule without a rating tag keeps the level gosec wrote.

What a #nosec becomes

gosec reports it as a SARIF suppression of kind inSource, which Draugr keeps and marks with origin: tool. It is the weakest of the three origins and counted apart from them: a comment beside the line was written by whoever was editing the file, and nothing about it went past a second person.

/* #nosec G204 -- the name is a constant in this program, reviewed 2026-09-22 */
_ = exec.Command("sh", "-c", name)

Opt-in

gosec is Go-only, so it doesn't run by default. Enable it under the sast control, where it runs beside Semgrep:

config:
  controls:
    sast:
      enabled: true
      gosec:
        enabled: true

The same key works as a per-component override. Only enable gosec for Go components: a repository whose every module holds no Go package is a caveat for each go.mod and nothing analyzed.

Saga options

config:
  controls:
    sast:
      gosec:
        enabled: true
        exclude: [G104]        # rules that do not apply to this codebase
        tags: [integration]    # code behind a build tag gosec does not build, it does not analyze
OptionWhat it does
includeRun only these rule IDs (-include).
excludeSkip these rule IDs (-exclude).
tagsGo build tags to compile with (-tags).

gosec's -severity and -confidence floors are deliberately not exposed. Both drop findings inside the tool, where Draugr cannot mark them suppressed or record who accepted them. A finding you have judged belongs in config.exclude, which keeps it in the report with your reason; what should fail a build is the gate's decision. Selecting rules is a different statement. That a check does not apply to this codebase at all.

Notes

  • Integration mode: exec over a local checkout; gosec + git must be on PATH (draugr tools install gosec provisions a pinned, SHA-256-verified copy).
  • gosec loads a module's packages with go, so Go must be on PATH too. Without it gosec exits 0 having analyzed nothing, and the scan fails the control with gosec needs Go, and no `go` is on PATH rather than reporting the module clean. A tree holding no module runs nothing and needs no Go.
  • gosec and govulncheck take turns loading Go packages across the whole run, so this scanner's job can wait for another load to end; the wait is in stats.orderWaitsMs.
  • gosec signs its releases with a key-based cosign bundle; Draugr's identity-based signature verification (used for Trivy) doesn't cover that yet, so tools install verifies gosec by SHA-256 only for now.

Data

Nothing. gosec's rules are compiled into its binary.