What the gate does
The gate is a one-shot scan of the dependency manifests in the repository. It parses
package-lock.json, requirements*.txt, Pipfile.lock,
poetry.lock and go.sum, sends the resolved package list to
/api/v1/proxy/check/batch, and gets back one verdict per package: allow, alert,
quarantine or block. The verdict comes from the same six detection signals the install-time proxy
uses, so a package that would be refused on a developer laptop is refused in CI too.
It runs on every push and pull request that touches a manifest, in about the time it takes to
install the CLI. On GitHub it is one uses: line. Dependabot pull requests run in advisory mode because they cannot read secrets.
Or let the CLI write the file for whichever provider your repository uses:
pip install --upgrade cyberxyz-scanner # 1.4.56 or later
cd your-repo
xyz ci init # detects GitHub, GitLab or Azure from origin and writes the pipeline file
xyz ci init --fail-on quarantine
pip install --quiet cyberxyz-scanner
xyz depalert scan --package-lock package-lock.json --fail-on block
xyz depalert scan --requirements requirements.txt --fail-on block
xyz depalert scan --go-sum go.sum --fail-on block
# ad hoc, no lockfile
xyz depalert scan -p axios@1.14.1 -p lodash@4.17.21 --fail-on block
Verdicts and exit codes
The build fails on any exit code above zero. The gate fails closed: if the backend cannot be reached, the scan exits 4 rather than letting an unchecked package through.
0 all packages allowed
1 BLOCK confirmed malicious version in the manifest
2 QUARANTINE day-zero anomaly, held for review
3 ALERT advisory match, suspicious but not confirmed
4 ERROR backend unreachable, refusing to fail open
GitHub Actions
Add XYZ_API_KEY under Settings, Secrets and variables, Actions, then commit
this workflow as .github/workflows/xyz-depalert.yml. The
CyberXYZ DepAlert action from the GitHub Marketplace installs
the CLI, finds your manifests and fails the build on a bad verdict.
name: CyberXYZ gate
on: [push, pull_request]
jobs:
depalert:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: CyberXYZSecurity/depalert-action@v1
with:
api-key: ${{ secrets.XYZ_API_KEY }}
fail-on: block # block | quarantine | alert
The action exposes verdict and exit-code outputs and sets XYZ_VERDICT for later
steps. Pin to @v1 for the moving major, @v1.0.0 for the exact release, or the commit SHA
if your policy requires it. Without the action, the same gate as plain steps:
name: XYZ Dependency Security Gate
on:
push:
branches: ["main", "master", "develop"]
paths: ["package-lock.json", "yarn.lock", "requirements*.txt", "Pipfile.lock", "poetry.lock", "go.sum"]
pull_request:
branches: ["main", "master", "develop"]
paths: ["package-lock.json", "yarn.lock", "requirements*.txt", "Pipfile.lock", "poetry.lock", "go.sum"]
workflow_dispatch:
jobs:
xyz-depalert:
runs-on: ubuntu-latest
# Dependabot PRs cannot read secrets: advisory mode, never a red build
continue-on-error: ${{ github.actor == 'dependabot[bot]' }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: "3.11", cache: "pip" }
- run: pip install --quiet cyberxyz-scanner
- name: Scan package-lock.json
if: hashFiles('package-lock.json') != ''
env:
XYZ_API_KEY: ${{ secrets.XYZ_API_KEY }}
XYZ_API_URL: https://api.cyberxyz.io
run: xyz depalert scan --package-lock package-lock.json --fail-on block
- name: Scan requirements.txt
if: hashFiles('requirements.txt') != ''
env:
XYZ_API_KEY: ${{ secrets.XYZ_API_KEY }}
XYZ_API_URL: https://api.cyberxyz.io
run: xyz depalert scan --requirements requirements.txt --fail-on block
XYZ_API_KEY secret.
GitLab CI
Add XYZ_API_KEY under Settings, CI/CD, Variables as masked and protected.
Copy the template as .gitlab-ci.yml, or include it from an existing pipeline. It runs three
parallel jobs, one per ecosystem, and each only fires when its manifest exists.
include:
- remote: 'https://raw.githubusercontent.com/CyberXYZSecurity/XYZ-Exploitability-Scanner/main/integrations/gitlab-ci/cyberxyz-supply-chain.gitlab-ci.yml'
# or inline
image: python:3.11-slim
stages: [scan]
variables:
XYZ_API_URL: "https://api.cyberxyz.io"
XYZ_FAIL_ON: "block"
before_script:
- pip install --quiet cyberxyz-scanner
xyz_depalert_npm:
stage: scan
rules: [{ exists: ["package-lock.json", "yarn.lock"] }]
script:
- xyz depalert scan --package-lock package-lock.json --fail-on "$XYZ_FAIL_ON"
xyz_depalert_python:
stage: scan
rules: [{ exists: ["requirements.txt", "Pipfile.lock", "poetry.lock"] }]
script:
- for f in requirements.txt requirements-dev.txt Pipfile.lock poetry.lock; do
if [ -f "$f" ]; then xyz depalert scan --requirements "$f" --fail-on "$XYZ_FAIL_ON"; fi; done
xyz_depalert_go:
stage: scan
rules: [{ exists: ["go.sum"] }]
script:
- xyz depalert scan --go-sum go.sum --fail-on "$XYZ_FAIL_ON"
The template uses the python:3.11-slim image and runs unchanged on any Docker-executor
runner. On shell executors, drop the image: line and make sure python3 is on the path.
Azure DevOps
Add XYZ_API_KEY as a secret pipeline variable, then wire the template as the build pipeline.
It mirrors the GitHub workflow: same triggers, same manifests, same XYZ_FAIL_ON variable, and
runs on the ubuntu, macOS and Windows hosted agents.
trigger:
branches: { include: [main, master, develop] }
paths: { include: ["package-lock.json", "yarn.lock", "requirements*.txt", "Pipfile.lock", "poetry.lock", "go.sum"] }
pool: { vmImage: 'ubuntu-latest' }
variables:
XYZ_API_URL: 'https://api.cyberxyz.io'
XYZ_FAIL_ON: 'block'
steps:
- task: UsePythonVersion@0
inputs: { versionSpec: '3.11', addToPath: true }
- script: pip install --quiet cyberxyz-scanner
- script: |
if [ -f package-lock.json ]; then xyz depalert scan --package-lock package-lock.json --fail-on "$XYZ_FAIL_ON"; fi
if [ -f requirements.txt ]; then xyz depalert scan --requirements requirements.txt --fail-on "$XYZ_FAIL_ON"; fi
if [ -f go.sum ]; then xyz depalert scan --go-sum go.sum --fail-on "$XYZ_FAIL_ON"; fi
env:
XYZ_API_KEY: $(XYZ_API_KEY)
Choosing the threshold
XYZ_FAIL_ON sets the narrowest verdict that turns the build red.
block confirmed malicious only start here
quarantine plus day-zero anomaly signals recommended once you have a baseline
alert plus CVE, GHSA and OSV advisories strictest
Start with block, watch the dashboard for a week, then tighten. Quarantine is where the
day-zero catches live: new-dependency injection, version jumps and lifecycle-hook changes that have no
advisory yet.
What you see in the dashboard
The CLI detects the CI provider from its environment and tags every scan with the pipeline, job and
runner. GitHub runs appear as gha/<owner>/<repo>, GitLab runs as
gitlab/<group>/<project>/<runner>, Azure runs as ado/<agent>.
Filter the Environments view by machine name to see every run from one project, and the proxy log for
exactly which package tripped which signal.
Live blocking on runners
The gate checks what the manifest says. To gate what actually gets fetched during
npm install, pip install or go get, put the install-time proxy on
the runner in a before_script, with an enrollment token from the dashboard so the job needs no interactive login. Blocks then return HTTP 403 from the registry itself and the
install aborts.
before_script:
- pip install --quiet cyberxyz-scanner
- xyz proxy setup --machine-name "ci/$CI_PROJECT_PATH" --enrollment-token "$XYZ_ENROLLMENT_TOKEN" --no-install-daemon
# npm, pip and go now resolve through proxy.cyberxyz.io for the rest of the job
What it does not do
Said plainly, so nobody discovers it in an incident.
- It does not watch network traffic or processes on the runner. It is a dependency gate, not runner hardening.
- It does not block on lockfile drift, only on the resolved packages present at scan time.
- Without the proxy step it does not intercept installs. It scans, then exits.
What is coming
- A GitLab CI component in the catalog, and check annotations on the pull request.
- Pull-request mode that gates only the dependencies the change introduced.
- Monitoring of popular third-party Actions for tag re-points and release tampering.
See the CLI reference for every xyz command, or
book a demo and we will wire the gate into your pipeline on the call.