CyberXYZ Gate · Docs

CyberXYZ Gate,
in your pipeline.

Two steps for any pipeline. Protect routes the job's own installs through the CyberXYZ proxy; scan reads your lockfiles, checks every package against the same engine that answers the install proxy, and fails the build on a bad verdict. GitHub Actions, GitLab CI and Azure DevOps.

  • 3// platforms
  • 2// modes: protect + scan
  • 0–4// exit codes
.github/workflows/xyz-depalert.yml
- uses: CyberXYZSecurity/depalert-action@v1
  with:
    api-key: ${{ secrets.XYZ_API_KEY }}
    mode: protect          # before your installs
- run: npm ci
- uses: CyberXYZSecurity/depalert-action@v1
  with:
    api-key: ${{ secrets.XYZ_API_KEY }}
    fail-on: block         # block | quarantine | alert

GitLab: gitlab.com/cyberxyz/depalert/scan@1.2.0 and protect@1.2.0

01 · overview

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, uv.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 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 xyz Scanner. On GitHub it is one uses: line. The gate checks what the lockfile says; to also stop a bad package while the job installs it, add the protect step before your install steps. Or let xyz Scanner write the gate for whichever provider your repository uses:

xyz ci init · shell
pip install --upgrade cyberxyz-scanner
cd your-repo
xyz ci init            # detects GitHub, GitLab or Azure from origin and writes the pipeline file
xyz ci init --fail-on quarantine
xyz depalert scan · shell
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
02 · example

What a blocked pull request looks like

A permanently open pull request on the action's own repository bumps an example app to faker@6.6.6, the release in which the maintainer gutted his own package: the tarball ships no code at all, so anything that requires it breaks. npm never removed it. Four years on it is still the latest tag, npm install faker succeeds, and the gate refuses it on every run. Open the pull request and click Checks to see it live. This is the job output, verbatim:

✕ dependency gate (examples/webapp) · failed in 15s · pull request #1 · chore(deps): bump faker to 6.6.6
CyberXYZ DepAlert verdict block (exit 1), see the scan output above
▸ Run CyberXYZSecurity/depalert-action@v1
  xyz depalert scan package-lock.json
  XYZ DepAlert — scanning 2 packages…

                        1 Threat(s) Detected
  ┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━┓
  ┃ Package@Version ┃ Decision ┃ Scores     ┃ Signals ┃ Safe Version ┃
  ┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━┩
  │ faker@6.6.6     │ BLOCK    │ CVSS 7.5   │       2 │ 5.5.3        │
  │                 │          │ XYZ 3.7    │         │              │
  │                 │          │ EPSS 0.59% │         │              │
  └─────────────────┴──────────┴────────────┴─────────┴──────────────┘
  1 clean  1 blocked  0 quarantined  0 alerted

  ✗ Gate FAILED: 1 threat(s) detected (worst: BLOCK)
  Error: verdict block (exit 1), see the scan output above
express@4.19.2 in the same lockfile passed. Safe version 5.5.3 is the last release before the sabotage. The job took 15 seconds including installing xyz Scanner.
03 · reference

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.

exit codes
0   all packages allowed
1   BLOCK       known malicious version, critical advisory or org block list
2   QUARANTINE  day-zero anomaly, held for review
3   ALERT       advisory match, suspicious but not confirmed
4   ERROR       backend unreachable, refusing to fail open
04 · github actions

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 xyz Scanner, finds your manifests and fails the build on a bad verdict.

.github/workflows/xyz-depalert.yml
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

To stop a malicious version while the job installs it, run the same action in mode: protect before the install steps and keep the scan after them (see Protect):

.github/workflows/build.yml · protect + scan
steps:
  - uses: actions/checkout@v4
  - uses: CyberXYZSecurity/depalert-action@v1
    with:
      api-key: ${{ secrets.XYZ_API_KEY }}
      mode: protect            # route this job's installs through the CyberXYZ proxy
      # network-lock: true     # also blackhole the public registries on the runner
      # strict: true           # fail if the proxy could not be configured
  - run: npm ci                # refused at install time if a version is blocked
  - uses: CyberXYZSecurity/depalert-action@v1
    with:
      api-key: ${{ secrets.XYZ_API_KEY }}   # mode: scan (default) gates on the lockfiles

The action exposes verdict and exit-code outputs and sets XYZ_VERDICT for later steps. Pin to @v1 for the moving major, @v1.2.0 for the exact release, or the commit SHA if your policy requires it. Without the action, the same gate as plain steps (this version also runs Dependabot pull requests, which cannot read secrets, in advisory mode):

.github/workflows/xyz-depalert.yml
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
Add it to your repository in one clickOpens GitHub's new-file page with the workflow above already filled in. Review, commit, then add the XYZ_API_KEY secret.
github.com/
05 · gitlab ci

GitLab CI

Add XYZ_API_KEY under Settings, CI/CD, Variables as masked and protected, then include the CyberXYZ DepAlert component from the CI/CD catalog. scan adds three jobs, one per ecosystem, and each only runs when its manifest exists. Inputs: fail_on, stage, working_dir, api_url, cli_version. The same component ships protect for the jobs that install dependencies (see Protect).

.gitlab-ci.yml
# one include, from the GitLab CI/CD catalog
include:
  - component: gitlab.com/cyberxyz/depalert/scan@1.2.0
    inputs:
      fail_on: block        # block | quarantine | alert

# or inline, without the component
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.

06 · azure devops

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.

azure-pipelines.yml
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)
07 · tuning

Choosing the threshold

XYZ_FAIL_ON sets the narrowest verdict that turns the build red.

XYZ_FAIL_ON
block        known malicious versions, critical advisories   start here
             on the installed version, your org block list
quarantine   plus day-zero anomaly signals                   recommended once you have a baseline
alert        plus other 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. Quarantine holds the version for human review; block refuses it outright.

08 · platform

What you see in the dashboard

xyz Scanner detects the CI provider from its environment and names the runner: GitHub runs as gha/<owner/repo>/<runner>, GitLab runs as gitlab/<group/project>/<runner>, Azure runs as ado/<repo>/agent-<id>. In your own script steps, add --upload to xyz depalert scan and each gate result is filed on the dashboard's XYZ Scan page under CI gates with that name (the Action and the GitLab component do not upload yet). Jobs that use protect register as ci/<provider>/<repo> (override with --machine-name), and every install they make appears in the Proxy Monitor, with the signal that decided it.

09 · protect

Protect: live blocking on runners

The scan checks what the lockfile says. Protect checks what the job actually fetches: run it early in the job and every later npm, yarn, pip, uv, Go and NuGet install goes through the CyberXYZ proxy, the same way it does on a laptop. A blocked version is refused by the proxy with HTTP 403 (a quarantined one with HTTP 451, or 403 on pip) and the install aborts. Recommended: protect before the install steps, scan as the gate.

  • 01
    GitHub ActionsCyberXYZSecurity/depalert-action@v1 with mode: protect (example under GitHub Actions).
  • 02
    GitLab CIinclude gitlab.com/cyberxyz/depalert/protect@1.2.0 and add extends: .cyberxyz-protect to the jobs that install dependencies, or - !reference [.cyberxyz-protect, before_script] when the job already has a before_script.
  • 03
    Azure DevOps and any other CIxyz ci protect with XYZ_API_KEY set.
.gitlab-ci.yml · protect
include:
  - component: gitlab.com/cyberxyz/depalert/protect@1.2.0
    inputs:
      network_lock: false     # true: also blackhole the public registries (needs root)
      strict: false           # true: fail the job when the proxy could not be configured
  - component: gitlab.com/cyberxyz/depalert/scan@1.2.0

build:
  extends: .cyberxyz-protect  # its before_script runs first
  image: node:20
  script:
    - npm ci                  # goes through the CyberXYZ proxy
xyz ci protect · any CI
pip install --quiet cyberxyz-scanner

eval "$(xyz ci protect --format shell)"     # GitLab, Jenkins, CircleCI, any shell
xyz ci protect --format github              # GitHub: appends to $GITHUB_ENV and masks the token
xyz ci protect --format azure               # Azure DevOps: ##vso[task.setvariable] lines

# options
#   --network-lock     also blackhole the public registries in /etc/hosts (root or passwordless sudo)
#   --strict           exit non-zero when protection could not be applied
#   --machine-name     default ci/<provider>/<repo>

If CyberXYZ cannot be reached, protect warns and the job carries on unprotected; pass --strict (strict: true) to fail it instead. The proxy token only lives in the job's environment: it is never written to a dotenv artifact. For a long-lived self-hosted runner, enroll the machine once instead, with an enrollment token from the dashboard and no background service: xyz proxy setup --enrollment-token "$XYZ_ENROLLMENT_TOKEN" --no-install-daemon.

10 · scope

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 protect step it does not intercept installs. It scans, then exits.
11 · next

What is coming

  • 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 xyz Scanner reference for every xyz command, or book a demo and we will wire the gate into your pipeline on the call.

CyberXYZ Gate FAQ

Questions, answered.

Does the gate need an API key?

Yes. The scan authenticates with an XYZ_API_KEY stored as a repository or group secret. A key with read and scan scope is enough; the gate never needs admin scope. Without a key the job fails closed rather than passing silently.

What happens when CyberXYZ is unreachable?

The gate fails closed. A build that cannot reach a verdict exits 4 and stops, rather than installing an unchecked dependency. If you would rather ship during an outage, let exit code 4 pass in your pipeline: the Action exposes an exit-code output, and GitLab accepts allow_failure with exit_codes: [4].

Will it break builds on packages that are merely old or unpopular?

No. The gate fails on the verdict, not on age or download count. Block means a known malicious version, a critical advisory that affects that exact version, or an entry on your organization's block list. Quarantine means day-zero anomaly signals that hold the version for human review. Alert is advisory and does not fail the build unless you choose that threshold.

How long does the scan add to a build?

It reads the lockfile and asks for one batched verdict, so a typical repository adds a few seconds. It never downloads or executes a dependency to reach its answer.

Does it work on GitLab and Azure DevOps too?

Yes. The same command runs anywhere. GitLab has a published CI/CD catalog component, GitHub has a Marketplace action, and Azure DevOps runs xyz Scanner directly in a script step.

Which ecosystems does the gate read?

npm (package-lock.json), PyPI (requirements.txt, Pipfile.lock, poetry.lock, uv.lock) and Go (go.sum) lockfiles. Maven projects are not gated yet; a single Maven package can be checked with xyz check PACKAGE VERSION -e maven. It reads the lockfile you already commit; there is no separate manifest to maintain.

// book a demo

Watch it block a real attack. Then run it on your own installs.

Book 15 minutes. We run real packages through CyberXYZ while you watch, including axios 1.14.1, a hijacked release that dropped remote-control malware in March 2026, and show you exactly what your team would see. Then we connect one laptop or one pipeline for a 30-day proof of value.

What happens next

  1. 01A 15-minute callReal packages, real verdicts, and your questions.
  2. 02Connect one laptop or one pipelineOne setup command, or one line in CI. Under an hour.
  3. 03A 30-day proof of value on your own installsEvery signal, no card.

Not ready for a call?

Check any package, free, and see the verdict we would give it.

npm

Try faker@6.6.6ua-parser-js@0.7.29express@4.21.2

Thanks! We'll be in touch.

Check your inbox. We'll reach out within 24 hours.

Where should we send the invite?

  • 15 minutes
  • Video call
  • Times in your time zone

We reply within 24 hours to set a time.

We only use these details to arrange the call. Privacy policy