CI/CD gate¶
Without an agent or an MCP client, a Jenkins, GitLab or GitHub job can block a deployment while
critical findings remain open. The security-review-ci gate command does it through the
controller REST API, with a ci token: it needs only Python 3
on the build agent (no database driver, no SQLModel, no database URL). The controller CLI
(security-review-controller) has no pipeline command.
security-review-ci gate --project portal --fail-on high
# exit code: 0 = passes, 1 = blocked, 2 = error (project/report not found, outside the token,
# controller unreachable, token refused)
# options: --report-id N to judge only one report
Pipeline client (security-review-ci)¶
security-review-ci is a lightweight client of the controller: it calls the pipeline routes of
the REST API (/api/v1/ci/gate, /api/v1/ci/sarif, /api/v1/ci/trend, and
/api/v1/ci/scan/code and /api/v1/ci/scan/deps for the scans) and opens no database. It only uses the Python standard library: install the package
(pip install security-review-mcp), or copy the single file
security_review_mcp/ci_client.py to the agent and run it with python3 ci_client.py.
| Sub-command | Role |
|---|---|
gate --project P [--fail-on S] [--report-id N] |
Gate of the project (see above) |
sarif --project P [--include-resolved] [-o FILE] |
SARIF 2.1.0 log of the project (SARIF export) |
trend --project P [--days N] [--granularity day\|week\|month] [--format markdown\|json] |
Trend report of the project (Trend report) |
scan-code [PATH] --project P [--save] … |
Sends the files of the repository to the controller, which scans them with its rules (Whole codebase scan) |
scan-deps FILE… --project P [--save] … |
Sends manifests and lockfiles to the controller, which checks them against its advisories (Dependencies) |
Common options: --url (default SECREVIEW_CONTROLLER_URL), --token-stdin (reads the token
on stdin instead of SECREVIEW_TOKEN), --cafile (CA of the controller certificate),
--allow-http, --timeout (seconds, default 60).
- The token is never accepted on the command line (process list, job log) and never written in an error message.
- Plain HTTP is refused except to
localhost(or with--allow-http), and so are credentials in the URL; redirects are not followed, so the token is only sent to the given controller. - The two variables are read from the environment only (the
.envfile is ignored). - The answers are bounded (64 MiB) and the control characters of the text output are removed.
ci token and visible projects¶
A ci token only reaches the pipeline routes: neither /mcp, nor the rest of the REST API,
nor the web interface. It is always restricted to projects: it sees the gate, the SARIF
log and the trend of the selected projects only, and only stores scan reports for them; another
project answers 404, like a project that does not exist. --project is
normalized like the project of a review: give the repository
name.
- In the web interface, Tokens screen: Usage CI/CD pipeline, then tick the Allowed projects. The secret is shown once.
- From the CLI:
security-review-controller token create --label jenkins-portal --role ci \
--project portal --project portal-api
Web sessions and api tokens (within the projects they read) and admin tokens may also call
the gate, SARIF and trend routes; mcp tokens may not. The scans, which store reports, need a ci or
admin token (403 otherwise, web sessions included).
Examples¶
// Jenkinsfile: the ci token is a "Secret text" credential
pipeline {
agent any
environment {
SECREVIEW_CONTROLLER_URL = 'https://secreview.example.com'
}
stages {
stage('Security gate') {
steps {
withCredentials([string(credentialsId: 'secreview-ci', variable: 'SECREVIEW_TOKEN')]) {
sh 'security-review-ci sarif --project portal -o security.sarif'
sh 'security-review-ci gate --project portal --fail-on high'
}
}
}
}
post {
always {
// Warnings Next Generation plugin
recordIssues tool: sarif(pattern: 'security.sarif')
}
}
}
# .gitlab-ci.yml
security-gate:
stage: test
image: python:3.14-slim
script:
- pip install security-review-mcp
- security-review-ci gate --project "$CI_PROJECT_NAME" --fail-on high
variables:
SECREVIEW_CONTROLLER_URL: https://secreview.example.com
# SECREVIEW_TOKEN: masked and protected CI/CD variable (ci token)
- name: Security gate
run: security-review-ci gate --project "${{ github.event.repository.name }}" --fail-on high
env:
SECREVIEW_CONTROLLER_URL: https://secreview.example.com
SECREVIEW_TOKEN: ${{ secrets.SECREVIEW_CI_TOKEN }}
A finding stops blocking when a person moves it to fixed, accepted or false_positive in
the web interface: the decision stays recorded in
the database with its reason and its approver. An agent can only propose a status
(propose_finding_status), which does not unblock the gate. An acceptance or a false positive
expires: from its expiry date, the finding blocks again.