eval() on untrusted input,
TLS verification disabled in code — rather than against a package list
(vulnerability scanning) or a declared resource (infrastructure scanning).
It runs as part of a repository scan, and it is off until you configure
it.
Enabling it
Setting
SAST_RULES alone is not enough: the analyser this feature runs is a
separate program, and it is deliberately not installed in the published
trustowl-backend image (see below). With no working analyser, the worker
reports static analysis as disabled at startup and in /system/status
rather than failing every repository scan — see
Troubleshooting scans.
/app/rules/sast is TrustOwl’s own vendored ruleset, already present in the
published image (just YAML pattern files — see below for why those ship even
though the analyser does not). Point SAST_RULES at your own path instead,
or a directory, to run additional or different rules.
Why the analyser is not in the published image
The static-analysis engine (Semgrep) is licensed LGPL-2.1-or-later, and this product’s licence gate treats that as strong copyleft — the publishedtrustowl-backend image carries none, by policy, and that stays true here.
Instead, backend/Dockerfile.sast is a build recipe: the published image,
plus a pinned Semgrep and git, that you build yourself. Building it is
an explicit choice about the licence terms you are accepting for your own
deployment, rather than a decision made silently on your behalf:
trustowl-backend:sast with SAST_RULES set, and
repository scans that request sast will run it. The API image can stay the
published one — only the worker needs the analyser.
Language coverage
The vendored ruleset ships one rule per language today, each proven against a real vulnerable fixture and a real clean fixture with the real analyser (backend/internal/e2e/sast_language_matrix_e2e_test.go):
This is a starting point, not a comprehensive scan of any language’s
vulnerability classes. See What this does not cover.
The ruleset version
Every static-analysis finding carries aruleset_version — a content digest
of the rule files that produced it, visible on the finding’s evidence in the
dashboard and the API. It answers “was this checked against the current
rules” without naming the analyser. It changes only when a rule’s content
changes, never on an ordinary rescan, and it is evidence rather than
identity: editing a rule does not re-open or duplicate findings it already
produced.
Suppressing a finding
Two ways, at different scopes:- Inline, at the match. Add a
// nosemgrep: <rule-id>(or the language’s own comment syntax) on the matched line, or a bare// nosemgrepto suppress every rule there. The analyser drops the match before it ever reaches TrustOwl, and the comment itself — reviewed in the same commit that introduced it — is the audit trail. - After the fact, in TrustOwl. Suppress the finding from its detail page or the findings list, with a required reason and an optional expiry. This works the same way for every finding kind; see Findings and their lifecycle.
What this does not cover
Said explicitly, because a scanner that is silent about its own gaps reads as having none:- No per-tenant custom rules.
SAST_RULESis one deployment-wide configuration; there is no UI or API to add a rule per organisation. - No SARIF import or export.
- No autofix and no pull-request annotations. A finding is reported to TrustOwl; nothing comments on or edits your pull request.
- No taint tracking or cross-file dataflow in the vendored ruleset — each rule is a single-pattern match. Semgrep OSS supports more than this; the vendored rules do not yet use it.
- Registry rule packs stay off by default (
SAST_REGISTRY_ENABLED=false). Semgrep’s own hosted packs carry licence terms of their own to evaluate before enabling them, and a registry fetch makes a scan’s ruleset depend on what a third party served that day — the opposite of a reproducible verdict.
Next
- Scanning — worker placement, concurrency, and the registry policy that governs every scan target.
- Troubleshooting scans — what
/system/statussays when the analyser is configured but not installed.

