semgrep
- Control:
sast - Tool: Semgrep, https://semgrep.dev (repo https://github.com/semgrep/semgrep)
- Status: ✅ implemented
- Target: source repository (
RepositoryTarget), checked out at the scanned revision - License / terms: LGPL-2.1 (copyleft). exec only, do not bundle or import. Semgrep Pro and some registry rules are separate/commercial; Draugr uses the OSS CLI with OSS/user-provided rules.
What it does
Checks out the component's repository, then runs
semgrep scan --sarif --json-output <file> --verbose --max-target-bytes 1000000 --timeout-threshold 3 --no-error --metrics=off --config p/default <dir>
to analyze the project's own source code for security bugs (injection, unsafe APIs, etc.). See
the SAST glossary entry.
--json-outputwrites the same results as JSON to a temporary file, which Draugr reads for the rule metadata the SARIF leaves out (see Severity) and then removes. It needs Semgrep 1.70.0 or later; an earlier Semgrep refuses the option and the scan reports the error.--verbosemakes the JSON report list the files Semgrep skipped, with the reason, which it leaves out otherwise. Draugr reads the list for unread files.--max-target-bytes 1000000skips files over 1 MB.--timeout-threshold 3stops running rules on a file after three timeouts.--no-errorkeeps the process successful when findings exist (findings live in the SARIF report, not the exit code; thesastcontroller judges severity).--metrics=offavoids sending scan telemetry to the Semgrep registry.--config p/defaultis the OSS default rule pack. Semgrep'sautoconfig is not used: it refuses to run with metrics disabled.
Links
- CLI reference: https://docs.semgrep.dev/cli-reference
- Rule registry: https://semgrep.dev/r
Notes
- Integration mode: exec over a local checkout; Semgrep +
gitmust be onPATH. - Semgrep's SARIF puts each finding's level in the rule's
defaultConfiguration.level(not on the result). Draugr's SARIF parser resolves a result's level from its rule, so ERROR/WARNING/INFO map through to error/warning/note correctly. The severity comes from the rule's rating where it states one (see Severity). - A
configpath on disk resolves against the directory Draugr runs in and reaches Semgrep as an absolute path, so it names the same file for every repository, including one scoped bypaths. - Rules on disk are reported under the id their file declares. With
configset to a rules file or directory, Semgrep prefixes each rule id with the directory it loaded the rule from, sono-evalin/home/alice/rules/x.yamlarrives ashome.alice.rules.no-eval. Draugr removes the part that comes from the configured path, and the rule isno-evalon every machine. A rule in a subdirectory of a configured directory keeps the subdirectory (nested.no-eval). Exclusions anddraugr diffmatch on this id.
Unread
Semgrep finishes with exit 0 and reports findings only from the files it checked. Draugr reads two
gaps from the JSON report and lists each file as an unread-files caveat:
- A file over 1 MB, which Semgrep lists under
paths.skippedwith the reasonexceeded_size_limitand does not scan:over 1 MB, not scanned. Semgrep applies the limit before it matches files to rule languages, so Draugr lists the file only where a configured rule reads its language, by extension, file name or#!line. A ruleset Semgrep fetches is taken to read every file. - A rule that timed out on a file, an entry in
errorsof typeTimeout:1 rule timed out, not checked: "x". When three rules time out on one file, Semgrep runs no further rule on it, and the reason reads3 rules timed out, other rules not run.
A file Semgrep was told to skip, by .semgrepignore, .gitignore or a component's ignore:, is
not listed. sast has what to do about each and how to fail the
run on them.
Severity
A result's severity comes from its rule's impact and likelihood, each LOW, MEDIUM or
HIGH, by the table below. Semgrep publishes both in the rule's metadata, with a confidence and a
subcategory of vuln, audit or secure default. Its SARIF keeps only the confidence, as a tag,
and gives most of the default pack the level error or warning whatever the rule finds, so Draugr
reads the metadata from the JSON report the same run writes.
| impact \ likelihood | LOW | MEDIUM | HIGH |
|---|---|---|---|
HIGH | medium | high | critical |
MEDIUM | low | medium | high |
LOW | low | low | medium |
- One level lower, never below low, for a rule with confidence
LOWor subcategoryaudit, and one level where it is both. Semgrep defines aLOWconfidence rule as one that expects a fair amount of false positives, and an audit rule as one for code auditors. - No change for confidence
MEDIUMorHIGH, or subcategoryvulnorsecure default. - The level for a rule that does not state both impact and likelihood, which a rule written by
hand usually does not, with
erroras high,warningas medium andnoteas low.
The cells are the overall-severity table of the OWASP Risk Rating Methodology, with its lowest cell, Note, read as low. Semgrep says a registry rule's severity is assigned from its likelihood and impact, and publishes no table for the combination.
The report prints the rating on the finding's evidence line, impact high · likelihood high, with
confidence low or subcategory audit added where either applies, so a line without them is the
table's cell. results.sarif keeps the level Semgrep wrote and adds the result property
draugr/rating, which holds the severity, that line, and each of the four fields the rule states,
with Semgrep's value.
A result a nosemgrep comment suppressed has no entry in the JSON report, so it takes the rating of
another result of the same rule, and keeps its level where there is none.
Data
The rule pack, from semgrep.dev. p/default is resolved on every invocation: there is
no local cache to warm, so a run contacts the registry once per job rather than once, and a
machine with no network cannot run this scanner on the default pack.
--config against rules on disk is the way to run it without the registry.
Under --offline, Semgrep does not run on a ruleset it would fetch, and the sast control reports
an error naming the setting:
sast ERROR did not run
semgrep: cannot run offline: config.controls.sast.semgrep.config is unset, and Semgrep fetches
its default, p/default, from semgrep.dev; set it to a rules file or directory on disk
Semgrep fetches the ruleset when config is unset, a registry id (p/…, r/…, s/…), auto, a
product name such as code, or a URL. A rules file or directory on disk runs as normal.