Quality Gates
A quality gate is a hard pass/fail verdict attached to every scan. It answers one question: “Is this codebase safe enough to ship?” — and stops a CI build automatically if the answer is no.
Without a gate, teams see findings but nothing enforces action. With a gate, a PR with a new critical vulnerability cannot merge until it is fixed.
How it works — step by step
The security score
The score starts at 100 and loses points for each finding. Deductions are weighted by severity so a single critical has the same impact as ten medium findings:
score = 100
− (critical_count × 25)
− (high_count × 10)
− (medium_count × 3)
− (low_count × 1)
# clamped to [0, 100]
# Examples:
# 0 findings → 100 Grade A
# 1 critical → 75 Grade C
# 1 critical + 2 high → 55 Grade F
# 3 high + 5 medium → 55 Grade F
# 10 medium only → 70 Grade CThe score maps to a letter grade displayed in every scan summary and PR comment:
| Grade | Score | Meaning | Typical state |
|---|---|---|---|
| A | 90–100 | Excellent | No critical or high findings |
| B | 80–89 | Good | A handful of low findings only |
| C | 70–79 | Acceptable | Some medium findings — needs monitoring |
| D | 60–69 | Needs work | High findings present — fix before ship |
| F | 0–59 | Failing | Critical or many high findings — block now |
Gate thresholds
A gate can have up to four checks. All four run every time — if any one fails, the gate fails. You can raise or lower each threshold independently to match your team's risk tolerance.
| Check | Default | What it tests | CLI flag |
|---|---|---|---|
| max_critical | 0 | Number of critical findings ≤ threshold | --gate-max-critical |
| max_high | 3 | Number of high findings ≤ threshold | --gate-max-high |
| max_medium | 10 | Number of medium findings ≤ threshold | --gate-max-medium |
| min_score | 70 | Security score ≥ threshold | --gate-min-score |
Using the gate — CLI
The score is always computed and shown. Add --gate to enforce it — the CLI exits with code 1 if any threshold is violated.
Quickstart
codescan scan --dir ./src --gateTerminal output — gate passed
────────────────────────────────────────────────────────────
SCAN SUMMARY
────────────────────────────────────────────────────────────
Files scanned : 12
Files skipped : 0
Duration : 8.4s
HIGH : 2
MEDIUM : 4
LOW : 1
Total : 7
SCORE : 83/100 Grade B
QUALITY GATE : ✓ PASSED
────────────────────────────────────────────────────────────Terminal output — gate failed
────────────────────────────────────────────────────────────
SCAN SUMMARY
────────────────────────────────────────────────────────────
Files scanned : 12
Files skipped : 0
Duration : 9.1s
CRITICAL : 1
HIGH : 4
MEDIUM : 6
Total : 11
SCORE : 51/100 Grade F
QUALITY GATE : ✗ FAILED
✗ Critical: 1 (max 0)
✗ High: 4 (max 3)
✗ Score: 51/100 (min 70)
────────────────────────────────────────────────────────────
Quality gate failed — 3 violation(s). Score: 51/100 (Grade F)
exit code 1Custom thresholds per run
codescan scan --dir ./src --gate \
--gate-max-critical 0 \ # zero tolerance for criticals
--gate-max-high 2 \ # allow up to 2 highs
--gate-max-medium 5 \ # allow up to 5 mediums
--gate-min-score 80 # require at least BPersisting config — .codescanrc.json
Instead of repeating flags on every run, drop a .codescanrc.json in your project root. The CLI reads it automatically. CLI flags always override the file.
// .codescanrc.json
{
"gate": {
"maxCritical": 0,
"maxHigh": 2,
"maxMedium": 5,
"minScore": 80
}
}
# Now this is enough — rc file supplies the thresholds:
codescan scan --dir ./src --gateGitHub Actions integration
The full CI setup does four things in one step: scans the code, enforces the gate, exports a SARIF file to the GitHub Security tab, and posts a formatted comment directly on the PR.
Secrets setup
Add one secret to your GitHub repository: Settings → Secrets → Actions → New repository secret
Workflow file
# .github/workflows/codescan.yml
name: CodeScan Security
on:
pull_request: # runs on every PR
push:
branches: [main] # runs on merges to main
jobs:
security:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write # required to upload SARIF
pull-requests: write # required to post PR comment
steps:
- uses: actions/checkout@v4
- name: Run CodeScan
run: |
npx codescan-flowlog scan \
--dir ./src \
--gate \
--sarif-out results.sarif \
--pr-comment \
--save-history \
--fail-on high
env:
CODESCAN_TOKEN: ${{ secrets.CODESCAN_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Upload findings to GitHub Security tab (runs even if gate fails)
- name: Upload SARIF
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarifWhat happens on a PR
- PR comment posted automatically — score, grade, gate status, and a table of the top findings appear as a comment the moment the scan finishes
- Findings shown inline on the diff — SARIF upload surfaces each finding at the exact line in the Files Changed tab
- Gate blocks the merge — if the gate fails the job exits with code 1 and GitHub marks the required check as failed
- History saved — the scan result is stored to your account so you can track the score trend with
codescan history
Example PR comment
| Severity | Count |
|---|---|
| 🟠 High | 2 |
| 🟡 Medium | 4 |
| 🔵 Low | 1 |
SARIF export — standalone
Export a SARIF 2.1.0 file from any scan without running the full CI workflow:
codescan scan --dir ./src --sarif-out results.sarifSARIF files can be uploaded to GitHub, imported into VS Code extensions, or consumed by any tool that supports the OASIS SARIF 2.1.0 standard. SARIF export requires the Starter plan or above.
Scan history & trend tracking
Every scan run with --save-history is stored to your account. Run codescan history any time to see how your score has moved:
# Save history during a scan
codescan scan --dir ./src --gate --save-history
# View the trend
codescan history --limit 10
DATE SCORE GRADE VULNS CRIT TARGET
────────────────────────────────────────────────────────────
13 May 2026 09:15 88/100 B 3 0 src
12 May 2026 14:30 72/100 C 9 0 src
11 May 2026 11:00 61/100 D 14 1 src
Trend: ↑ +27 pts vs 3 scans agoEach row is one scan run. The trend line at the bottom compares the latest score against the oldest in the window — so you can see at a glance whether the codebase is getting safer or drifting the wrong way.
Gate REST API
Call the gate directly from any custom tool or script — no CLI required. Pipe the results from /api/scan straight into /api/gate:
# Request
POST https://codesscan.com/api/gate
Content-Type: application/json
{
"results": [ /* FileScanResult[] from /api/scan */ ],
"config": { # all fields optional — defaults shown
"maxCritical": 0,
"maxHigh": 3,
"maxMedium": 10,
"minScore": 70
}
}
# Response — gate passed
{
"score": 83,
"grade": "B",
"passed": true,
"violations": [],
"counts": { "critical": 0, "high": 2, "medium": 4, "low": 1 },
"summary": { "files": 12, "totalVulnerabilities": 7 }
}
# Response — gate failed
{
"score": 51,
"grade": "F",
"passed": false,
"violations": [
{ "rule": "max_critical", "actual": 1, "threshold": 0, "message": "Critical: 1 (max 0)" },
{ "rule": "min_score", "actual": 51, "threshold": 70, "message": "Score: 51/100 (min 70)" }
],
"counts": { "critical": 1, "high": 4, "medium": 6, "low": 0 },
"summary": { "files": 12, "totalVulnerabilities": 11 }
}History REST API
# List your last N scans (max 100)
GET /api/scan/history?limit=20
Authorization: Bearer <token>
# Save a scan result manually
POST /api/scan/history
Authorization: Bearer <token>
{ "results": [...], "target": "./src", "source": "cli" }