ci(sast): add FerrLabs OpenGrep ruleset from the org audit - #190
Conversation
There was a problem hiding this comment.
Reviewed the new sast/ferrlabs.yml ruleset. The three regex-based rules match the counts verified in the PR description, and the other three AST rules (native-browser-dialog, login-timing-oracle, jwt-validation-without-issuer) look structurally sound as pattern syntax. One correctness issue found in the fourth unverified AST rule — see inline comment (Blocking). Nothing else blocking; the known fg-game-card false positive on local-ui-primitive is already called out and fine to tune later since this is advisory-only for now.
| - pattern: $Q.fetch_all(...) | ||
| - pattern-not-inside: | | ||
| $S = "...LIMIT..."; | ||
| ... |
There was a problem hiding this comment.
Blocking: this exclusion can't do what the message promises. $S is a fresh metavariable never unified with $Q, so pattern-not-inside only checks whether some string assignment containing the literal text ...LIMIT... exists in the enclosing scope — not whether the string actually passed to $Q.fetch_all(...) contains LIMIT. Separately, ... inside a quoted-string pattern is matched as literal dot characters by the Rust matcher, not as an ellipsis wildcard, so in practice this clause will essentially never match anything real. Net effect: the rule fires on every fetch_all(...) under routes/public*.rs regardless of whether a LIMIT is present — the opposite of advisory-quality (guaranteed noise, not a missed case).
Since fetch_all's query is usually built through a query-builder rather than a raw string literal, string-content matching probably isn't the right tool here anyway. Worth reworking (e.g. metavariable-pattern tied to $Q, or dropping the exclusion for a manual follow-up pass) before relying on this one for signal — this is one of the four AST rules the PR description flags as unverified, so flagging it now per that ask.
There was a problem hiding this comment.
Follow-up on the new commit (0a94eff), which wires sast/ferrlabs.yml into reusable-security-scan.yml by default. One nit inline on the newly added code, matching an existing code-scanning alert. The rest of the wiring looks correct: REF is properly passed via env:, a failed curl degrades to a warning instead of failing the step, and the existing OpenGrep exit-code handling is untouched.
The Blocking finding from my previous review (the ferrlabs-unbounded-public-query rule's broken pattern-not-inside exclusion, sast/ferrlabs.yml#L86) is unchanged in this commit — still open, not re-stating details.
Co-authored-by: ferrfleet[bot] <307549260+ferrfleet[bot]@users.noreply.github.com>
Closes #189
Adds a FerrLabs OpenGrep ruleset and wires it into the shared security scan, so the patterns the org audit found are checked on every push instead of once.
Two parts
1.
sast/ferrlabs.yml— six rules, each encoding a defect that recurred in more than one repo:native-browser-dialogstringly-typed-domainjwt-validation-without-issuerunbounded-public-queryhardcoded-plan-limitlocal-ui-primitive2.
reusable-security-scan.ymlapplies it by default. 19 repos already call this workflow, so they all pick the rules up with no change on their side — the ruleset is passed as a second-falongside whateversast-configresolves to, rather than replacing it.Two new inputs:
ferrlabs-rules(defaulttrue) — opt out per repoferrlabs-rules-ref(defaultmain) — pin to a SHA to freeze the rules for a repoThe ruleset is fetched into
$RUNNER_TEMP, not the workspace, so it is not itself part of the scanned tree. If the fetch fails the step warns and the scan continues on the community set — our own rules being unreachable must never break a security scan.Verified by running the engine
semgrep(OpenGrep is a fork of it, same rule format) was installed locally and every rule was executed against purpose-built fixtures.Config parses:
found 0 configuration error(s), and 6 rule(s).Positive cases — each fires at the right file and line:
Negative controls — no findings, as intended:
The three regex rules were separately run against the real files they were derived from:
stringly-typedfound 4 inFerrTrack/models.rs,hardcoded-plan-limitfound 1 inFerrTrack/rules.rs,local-ui-primitivefound 5 inFerrVault/editorial.ts.One rule was written, tested, and removed
login-timing-oracle(for FerrGrowth-Cloud#599) is not in this PR. It matched the vulnerable login and a correctly-fixed one usingdummy_hash— thelet ... else+...sequence pattern is too loose to distinguish them. A rule that flags correct code trains people to ignore the scanner, so it is better absent until it can be written precisely. #599 still tracks the underlying issue.Rollout note
fail-on-saststaysfalseby default, so this is advisory on merge — findings land in the Security tab, nothing blocks. Measure the noise before gating.Expect
local-ui-primitiveto need tuning: FerrGames'fg-game-cardis a legitimate product component that matches the pattern and is not a duplication of anything shared.