SAST in CI with Semgrep and custom rules
Add fast static analysis to your pipeline and write project-specific rules that catch your own recurring bugs.
Static analysis that only security teams understand gets disabled by week three. Semgrep matches code against patterns that look like the code itself — no proprietary query language, no hour-long scans. It ships thousands of community rules for injection, auth bypass, and dangerous APIs, and lets you encode your own recurring mistakes in a few lines of YAML. That combination is why SAST belongs in every merge request, not in a quarterly audit spreadsheet.
You will run semgrep ci on every MR, write a project-specific rule for a bug your team keeps repeating, baseline legacy findings so only new issues fail the build, and promote rules from WARN to ERROR once the backlog is clean. GitLab and GitHub both fit the same pattern — the broader scanning stack lives in Software supply chain security.
Introduce gates gradually. A scanner that fails on day one gets `--skip` comments by day three.
Run it on every merge request
Pin the Semgrep image or CLI version so rule packs and engine behavior do not shift under you mid-sprint. Use --error (or semgrep ci) so findings exit non-zero and block merge on protected branches. Upload SARIF to your Git host if you want inline annotations — developers fix faster when the finding sits on the exact line.
sast:stage: testimage: semgrep/semgrep:1.90.0script:- semgrep ci --config auto --errorrules:- if: $CI_PIPELINE_SOURCE == "merge_request_event"
semgrep --config auto .python.lang.security.audit.subprocess-shell-true app/tasks.py:42 subprocess.run(cmd, shell=True)ran 1,284 rules on 96 files, 3 findingsfix or baseline before turning on --error in CIWrite a rule for your own bug
Say your codebase must never build SQL with f-strings — a pattern code review catches until someone is tired on a Friday. Encode it once in YAML and Semgrep enforces it on every push. Custom rules are how SAST stops being generic noise and starts catching bugs only your architecture can have.
rules:- id: no-fstring-sqllanguages: [python]severity: ERRORmessage: Build SQL with parameters, not f-strings (injection risk).patterns:- pattern: cursor.execute(f"...")
Keep the signal high
Baseline existing findings with semgrep ci --baseline-commit main or a checked-in baseline file so legacy debt does not block every MR. Use nosemgrep comments sparingly with a ticket reference — every suppression should appear in the diff for review. Pair Semgrep with secret scanning and dependency checks so SAST is one layer, not the whole program. Review custom rules in pull requests the same way you review application code: a bad rule is a permanent false sense of safety.
Where this goes next
SAST catches dangerous code paths; supply-chain tooling catches dangerous dependencies and unsigned artifacts. Wire Semgrep beside Grype on SBOMs, gitleaks on secrets, and Cosign on images so one pipeline covers code, deps, and provenance. Software supply chain security connects those gates from commit to cluster admission.
Go deeper in a courseSoftware supply chain securitySAST, dependency and secret scanning, SBOMs, and signing end to end.View course