SAST
fremforge runs OpenGrep on every pull request and on each repository’s default branch. It runs on fremforge’s own scanners, so there is no workflow file to add. A pull request gets the fremforge/sast status and a comment. Findings are listed under Security › Findings › SAST (/<org>/_admin/security/findings/sast).
Languages
C, C++, C#, Go, Java, JavaScript and TypeScript, Kotlin, PHP, Python, Ruby, Rust, Scala, Swift and Bash.
Rules
Three rule sets are built into the scanner image. Nothing is fetched while a scan runs:
- GitLab sast-rules, MIT;
- the 0xdea rules for C and C++, MIT;
- fremforge’s own rules, MIT, for JavaScript and TypeScript, Bash, PHP, Ruby, Rust, Kotlin and Swift.
Your own rules
An organisation can add its own OpenGrep rules. fremforge/sast runs them with the built-in rule sets on every pull request and on each repository’s default branch, under the same mode and block severity, and on a pull request only what it adds blocks it.
Add them under Security › Policies › SAST rules, through the API, or with Terraform (below). Each rule is YAML with exactly one rule under rules:, and a test sample: code the rule must find.
rules:
- id: acme-no-eval
message: Do not pass data to eval(); use ast.literal_eval or a parser.
severity: ERROR
languages: [python]
pattern: eval(...)A rule must have:
- an
idof 2 to 63 lower-case letters, digits and hyphens, unique in the organisation (its findings are shown ascustom/<id>); - a
message, aseverity(ERROR,WARNINGorINFO, shown as High, Medium and Low) and 1 to 5languages: Bash, C, C++, C#, Dockerfile, generic, Go, HTML, Java, JavaScript, JSON, Kotlin, PHP, Python, Ruby, Rust, Scala, Swift, Terraform, TypeScript or YAML; - a pattern:
pattern,patterns,pattern-eitherorpattern-regex, ormode: taintwithpattern-sourcesandpattern-sinks.
Only pattern, metavariable, taint, paths, fix, options and metadata keys are allowed, so a rule cannot reach outside itself. YAML anchors and aliases are refused. At most 50 rules per organisation, 16 KiB each, with a test sample of at most 16 KiB.
A saved rule is Checking until the scanner has run it with the same opengrep, in the same fenced environment as your scans, on its own test sample. It becomes Active when it parses, finds something in the sample and finishes within 30 seconds (5 seconds per file); otherwise it is Rejected, with the reason. Only an active rule that is not switched off runs. Changing a rule or its sample checks it again; until then it does not run. You can switch a rule off without deleting it.
Findings of your rules appear with the built-in ones, in the pull request check and under Security › Findings › SAST, marked Custom with the rule’s name. They block like built-in findings: by their severity against the block severity below.
Rules are stored in fremforge and run from there. The scanner never fetches rules from a URL or a registry, and checking a rule makes no network call. The custom_rules_url setting is retired: it is no longer read, and a value other than null or "" is refused with custom-rules-url-removed.
API
GET /_app/api/v1/orgs/{org}/code-security/sast/custom-rules
GET /_app/api/v1/orgs/{org}/code-security/sast/custom-rules/{id}
POST /_app/api/v1/orgs/{org}/code-security/sast/custom-rules { "name": "No eval", "source": "<yaml>", "sample": "<code>", "enabled": true }
PUT /_app/api/v1/orgs/{org}/code-security/sast/custom-rules/{id}
DELETE /_app/api/v1/orgs/{org}/code-security/sast/custom-rules/{id}Listing needs findings:read; changes need policy:write. A new or changed rule comes back with status pending; the scanner’s verdict sets it to valid or invalid, with status_code (for example no-match or parse), status_detail (opengrep’s own message, when there is one) and status_reason (a sentence). A rule the API can refuse before the scanner sees it is answered 400 with the reason (for example one-rule or unknown-key); a used id is duplicate-id and the 51st rule limit-reached, both 409. Every change is in the audit log.
Terraform and OpenTofu
Each rule is a fremforge_sast_custom_rule (provider 0.5.0 and later). An apply waits for the scanner’s verdict and fails with the reason when the rule is rejected; set wait_for_verdict = false to skip the wait.
resource "fremforge_sast_custom_rule" "no_eval" {
org = "acme"
name = "No eval"
source = file("sast/no-eval.yaml")
sample = file("sast/no-eval.sample.py")
}custom_rules_url on fremforge_sast_settings is deprecated and no longer sent; remove it.
Settings
Under Security › Findings › SAST, or GET and PUT /_app/api/v1/orgs/{org}/code-security/sast/settings (PUT needs policy:write):
| Setting | Values | Default |
|---|---|---|
| SAST enabled for this org | on, off | on |
| Block severity | info, warning, error: findings at or above it fail fremforge/sast | error |
Whether a failing check blocks a merge also depends on the check’s mode under Security › Prioritise › Pull request checks and on branch protection. On a pull request, only findings the pull request introduces block: a finding that only moved to another line is not new.
Findings
Findings are ranked by confidence, then severity, then whether the scanner traced data from a source such as user input to the finding, then age. Findings in tests, examples, third-party or generated code, and findings of a rule people often dismiss, are listed lower; they are not dismissed. Dismiss a finding with a reason, one at a time or in bulk, or with POST .../code-security/sast/findings/{id}/dismiss. The list is also at GET /_app/api/v1/orgs/{org}/code-security/sast/findings.
AI fixes
For a short list of rules, AI fix asks your organisation’s own AI model for a fix pull request. It is off by default; turn it on under Security › Fix › AI fix. See AI fixes for SAST findings.