The merge request is green, then release is blocked by a secret or dependency CVE—usually because Semgrep, Trivy, and Gitleaks each own a job: three logs, three exit codes, no shared Finding, no shared suppressions.
Takeaway: Put SAST (static application security testing), secret scanning, and SCA (software composition analysis for deps/artifacts/images) into one scan job, normalize to one Finding model, then apply policy for
PASSED/FAILED/SKIPPED. Community edition is GPL-3.0—use, modify, and distribute freely (see the repo LICENSE). This path can replace a pile of standalone scanner scripts. It does not replace full-site DAST, host IDS/RASP, or enterprise asset vulns platforms.
Below is replace script sprawl + Semgrep migration.
1. Where teams get stuck
| Symptom | Usual cause |
|---|---|
| PR blocks SAST only; images scanned on main | Three jobs onboarded at different times |
| Same issue in Semgrep logs and Trivy CSV | No shared fingerprint / disposition |
Exit code 1 means “everything bad” |
Scripts treat all non-zero alike |
| Rule upgrades touch three YAML files | Rule versions not bound to one job model |
| Empty Findings but PASSED | Stub engines or empty vuln DB |
Shortest path: install engines → doctor non-stub → small repo sast+secret → Findings and gate visible in console → run in parallel for about a week, then delete standalone jobs.
2. Capability map
| Capability | Plain language | Default engine | Typical target |
|---|---|---|---|
sast |
Static dangerous patterns (injection, XSS, unsafe deserialization, …) | OpenGrep | Source / Git |
secret |
Hard-coded AK/SK, tokens, private keys, connection strings | Gitleaks | Source / Git (history optional) |
sca |
Known CVEs and license issues in deps, artifacts, images | Trivy | Directory deps, artifacts, images |
Quality (smells, complexity) can run separately and by default does not count toward the security gate. Do not treat SonarQube Scanner as the default security core—console/docs win for quality engines.
Primary target types: upload, Git repo, artifact URL, container image; CLI can also scan a local directory. The console is not meant to scan arbitrary server paths.
3. From three scripts to one gate
3.1 Before (sketch)
# Problem: three logs, three exit semantics, no unified governance
semgrep:
script: [semgrep ci --config auto]
trivy-fs:
script: [trivy fs --exit-code 1 .]
gitleaks:
script: [gitleaks detect --source . --exit-code 1]
3.2 After: one job
Command shape follows current angus-security --help; this is a copyable sketch:
export ANGUS_SECURITY_URL="https://security.example.com" # console onboarding wins
export ANGUS_SECURITY_TOKEN="***" # CI secret only—never commit
angus-security scan run \
--target-type DIRECTORY \
--path . \
--capabilities sast,secret,sca \
--fail-on high
Exit codes (when aligned with product docs; confirm on your CLI):
| Code | Meaning | CI handling |
|---|---|---|
0 |
Gate passed | Continue |
1 |
Scan OK, Findings over policy | Block merge/release; fix or suppress |
2 |
Execution error (engine/network) | Page ops—not a business vuln |
3 |
Config / auth error | Fix token/args—not a business vuln |
Gate also has SKIPPED (e.g. capability not covered)—skipped ≠ passed.
3.3 Minimal GitLab step (other platforms linked)
# .gitlab-ci.yml (sketch)
security-scan:
stage: security
script:
- angus-security tools doctor || true
- angus-security scan run --target-type GIT_REPO
--repo "$CI_REPOSITORY_URL"
--ref "$CI_COMMIT_SHA"
--capabilities sast,secret
--fail-on high
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
Layering: PR with sast+secret and a higher threshold; nightly/release add sca (artifact or image). GitHub Actions / Jenkins and full layering: CI integration Reference.
4. Migrating off Semgrep
OpenGrep targets Semgrep-like rule matching. Migrate as “rule pack → job capability → gate,” not a line-by-line rewrite of every rule.
| Semgrep habit | In the unified job |
|---|---|
semgrep ci --config auto / custom ruleset |
Enable sast; bind rule-pack version on project/job |
| PR scans changed files only | Shallower scan level (e.g. QUICK); full tree nightly |
--severity-threshold / custom exit |
Platform --fail-on / policy thresholds → PASSED/FAILED
|
| SARIF upload to GitHub | Export SARIF after the job from reports/Findings |
| Separate Gitleaks job | Add secret on the same job |
4.1 Parallel cutover (about a week)
- Keep old Semgrep stage while the new job runs.
- Same small repo once each: high/blocking volume should be in the same order of magnitude (not byte-identical counts).
- Enable
secret; confirm secrets no longer need a standalone Gitleaks job. - Sample 3–5 highs: fingerprints exist; suppressions possible (TTL in the disposition article).
- Split CI alerts for exit 1 vs 2/3.
- Delete standalone
semgrep/trivy-*/gitleaksstages; canary before addingscato release gates.
Language coverage follows the current rule pack—do not claim “all languages.” If you depend on a Semgrep-ecosystem-only third-party pack, verify OpenGrep coverage first.
5. Console check that you “folded into one”
Text-only path:
- Open the scan job: completed; capability list matches what you enabled.
- Findings filter by engine/capability—stop grepping three external logs.
- Gate on the summary is
PASSED/FAILED/SKIPPEDand matches CI exit semantics (1 = over gate, not 2/3). - For leadership/audit, export a report snapshot—do not screenshot the Findings home (see report-export Reference).
6. Failure table and boundaries
| Symptom | Cause | Fix |
|---|---|---|
| Empty Findings + PASSED | Stub engines or empty DB |
doctor non-stub; SCA false green → vuln DB article |
| Still three jobs | New job added, old not removed | Delete after parallel week |
| Exit 2 treated as vuln | Engine missing / bad token | Separate gate fail vs exec fail |
| Secret flood | Fixtures / docs examples | Suppressions Reference |
| Repo ingest treated as full governance | Repo light gate ≠ Security depth | Repo integration Reference |
Out of scope: full-site DAST, RASP/host IDS, Qualys-class asset inventory, “scan equals MLPS pass,” AI auto-opening fix PRs.
FAQ
Does this fully replace Semgrep OSS?
On the “CI rule match + unified gate” path, yes for script sprawl. Verify third-party packs before migrating.
Is community edition enough to prove the path?
GPL-3.0 community can run install-engines + first scan. Seats/quotas/commercial follow console/docs—no pricing here.
How is this different from “Sonar as security”?
Quality analysis ≠ security pattern match. Security path is OpenGrep / Gitleaks / Trivy; quality gates stay separate by default.
Keep old SARIF pipelines?
Govern on platform Findings; export SARIF when IDE/tickets need it.
Summary
- Three scripts → one job, one Finding model, one gate.
-
doctor→sast+secret→ parallel week → then delete old jobs; addscafor release. - Split “over gate” from “did not run.”
- Not a DAST/host replacement; Repo light gate ≠ Security depth.
Reference articles
- What is AngusSecurity: https://www.anguskit.com/en/blog/what-is-angussecurity-capabilities-and-boundaries
- CLI first scan: https://www.anguskit.com/en/blog/angussecurity-cli-first-scan-guide
- Offline engines and doctor: https://www.anguskit.com/en/blog/angussecurity-offline-engines-doctor-checklist
- CI integration: https://www.anguskit.com/en/blog/cicd-security-scanning-gitlab-github-jenkins
- Finding suppressions: https://www.anguskit.com/en/blog/angussecurity-finding-suppress-disposition-guide
- Report snapshots: https://www.anguskit.com/en/blog/security-scan-report-snapshot-export
- Intranet vuln DB: https://www.anguskit.com/en/blog/intranet-vuln-db-offline-update-rollback
- AngusRepo artifact gate: https://www.anguskit.com/en/blog/angussecurity-angusrepo-artifact-gate-integration
- Ask about highs, findings, and gates via MCP: https://www.anguskit.com/en/blog/angussecurity-mcp-server-design
- Docs: https://www.anguskit.com/en/docs/security
Top comments (0)