Skip to content
Foxx Cyberfoxxcyber/docs

CI Guardrails

The pipeline that runs on every change — lint, type-check, tests, container and dependency scanning, and an SBOM — with the gates that actually block a merge.

Reading rules isn't enough; rules have to be enforced by machines that don't get tired and don't skip a step under deadline. Every Foxx Cyber repository runs a CI pipeline where a rule violation fails the build. This page describes the real pipeline for HLE-AIO; other products run the same shape adapted to their stack.

The pipeline, stage by stage

StageJobWhat it doesBlocks merge?
testqualityProduction build (also regenerates generated route code), lint, type-check, format-checkYes
testunit-testsThe full Vitest suite (under Node; the suite mocks the database boundary and runs with a dummy connection string)Yes
testinvariant-gateGreps only the lines the merge request adds for lint/type/scanner suppressions, unsafe HTML sinks, string-built SQL and skipped testsYes (MR pipelines)
testsemgrep-sast, secret_detectionGitLab's SAST and Secret Detection templates, run on every MR (AST_ENABLE_MR_PIPELINES)No — GitLab ships both allow_failure: true; findings are read in the MR widget and triaged
testtrivy-repo-scanFilesystem scan for vulnerable dependencies, misconfigurations, and secrets — HIGH,CRITICAL, fixable onlyYes (--exit-code 1)
buildbuild-imageBuilds the production container imageYes
buildgrype-image-scanScans the built image for vulnerabilities — --fail-on high, fixable only, against a documented ignore policyYes
buildsbomEmits a CycloneDX software bill of materials for the image, archived per buildProvenance artifact
releaseretag / release-semverPromotes the scanned image to latest/semver tags — only after the scans pass—

The ordering is the point. The image is only tagged for release after grype-image-scan passes, so a latest or versioned tag can never point at an image that failed its vulnerability gate. Scan first, promote second.

The gates that are real vs. advisory

Being honest about which checks actually block a merge is part of the method — a gate everyone believes is enforced but isn't is worse than no gate.

  • Real, blocking gates: quality, unit-tests, trivy-repo-scan (--exit-code 1), and grype-image-scan (--fail-on high). A failure in any of these stops the pipeline.
  • The forbidden-pattern check (invariant-gate: rule suppressions, raw SQL string-building, unsafe HTML sinks, skipped tests) runs against the lines the merge request adds and fails on a match — this is the machine backstop for the rulebook. Honest note: this job existed in HLE-AIO's original GitHub workflow, was missed in the move to GitLab, and was restored on 2026-09-09 when this page and the pipeline were reconciled. That reconciliation is itself the method: a docs claim about the pipeline is verified against the pipeline.
  • Advisory, not blocking: GitLab's SAST (semgrep) and Secret Detection template jobs. They run on every merge request and their findings are triaged, but GitLab ships them allow_failure: true. The blocking scanners are Trivy and Grype.
  • Provenance, not a gate: the CycloneDX SBOM is archived for supply-chain traceability; it informs, it doesn't block.

Vulnerabilities we accept, on purpose

Not every scanner finding can be fixed the day it appears — sometimes the fix is gated on an upstream release outside our control. When that happens we do not silence the finding. We record it as a documented, VEX-style accepted risk: what it is, why it isn't reachable in our usage, the upstream tracker, and a review-by date. The scanner keeps a curated ignore policy; suppression without written justification is not allowed.

The difference matters: a suppressed finding is invisible and rots. An accepted risk is visible, justified, and has a date on which someone has to look again.

Run multiple scanners

Trivy and Grype disagree sometimes — one will flag a package the other says isn't in the image, and resolving the disagreement is how you learn what's actually shipping. A single scanner is a single point of failure. Running a filesystem scanner and an image scanner, with an SBOM to cross-check against, catches things any one of them alone would miss.

Branch protection from day one

Even on a solo repository, the default branch takes no direct pushes; every change goes through a merge request with the required checks above. Zero required human approvals is fine when you're the only developer — the value isn't the review ceremony, it's the mandatory check suite that the AI's output and my own commits are equally subject to.

The pipeline templates and the CI/CD components we include are published in the Vibe Sec Ops repository, Apache-2.0, along with the evidence, accepted-risk and POA&M formats.

The HLE-AIO case study shows these gates in action during a real pre-release audit, alongside the deeper manual and multi-agent review passes that run on top of the automated gates before a release.

Last updated September 9, 2026