Skip to content

Back to the catalog

Control Which Package Registries Your CI Jobs and Developer Machines Use
stepsecurity.ioarticle

Control Which Package Registries Your CI Jobs and Developer Machines Use

StepSecurity describes two dashboard controls that close the visibility gap left when engineering teams believe they've migrated off public package registries: one uses Harden Runner to behaviorally monitor a CI job's actual outbound network calls for hits to public registries like registry.npmjs.org or pypi.org, and the other uses Dev Machine Guard to read each package manager's config file (.npmrc, pip.conf) on every developer machine to report its effective registry. It ties this to 2026 incidents — malicious axios versions on npm, a credential stealer hidden in two litellm PyPI releases, and a republished @bitwarden/cli that harvested SSH keys and GitHub tokens to inject malicious CI workflows — arguing developer machines, not just CI, are the entry point attackers use. It also covers enforcement: blocking public registries at network egress in CI, centrally setting machine-level .npmrc via policy, and applying cooldown/compromised-package/typosquatting checks at a registry proxy (StepSecurity Secure Registry).

TL;DR StepSecurity details CI network-egress and developer-machine config checks that catch package installs sneaking around an internal registry, motivated by the axios, litellm, and @bitwarden/cli supply-chain compromises.
Document

What it is

A StepSecurity blog post detailing two Overview-dashboard controls for verifying that CI jobs and developer machines actually route package installs through an internal registry (Artifactory, Nexus, Cloudsmith, Google Artifact Registry, or StepSecurity Secure Registry) rather than reaching public ones. Both are Severity: Medium. Control 1 ("Jobs should use a secure registry") is behavioral via Harden Runner; Control 2 ("Developer machines should use a secure registry") is config-based via Dev Machine Guard.

How it works

The controls answer the same question at the two points installs happen, using opposite methods:

  • Control 1 (behavioral): Harden Runner monitors a job's actual outbound network calls. It passes only if the network baseline contains no calls to registry.npmjs.org, pypi.org, registry-1.docker.io, etc. It does not parse the workflow file, so a job that sets a private registry in one place but falls through to a public default elsewhere still fails. You can drill from a failing job to the specific outbound call and the process that made it.
  • Control 2 (declarative): Dev Machine Guard is not a persistent agent and does not watch traffic. It reads the config files each package manager resolves from (.npmrc, pip.conf) and reports the effective registry plus its source.

The key operational detail the summary omits: Control 2 is evaluated per device-and-tool pair, not per device. One machine with npm, pnpm, bun, and pip produces four separate checks — a machine can be correct for npm and still hitting pypi.org for pip. It is also tenant-wide across all organizations, not a selected repo set.

What to do

When remediating Control 2, read the source column — it distinguishes three different fixes:

  • npm built-in default → nothing on the machine sets a registry at all.
  • User config → a config file sets it explicitly.
  • A tool explicitly configured to registry.npmjs.org → someone wrote the public registry in by hand.

For Control 1, treat failures on egress-locked-down jobs as real signal, not noise: the baseline records attempted calls including ones your network policy blocked. A failure means something in the job is still trying to reach a public registry even though it didn't get through.

Caveats

  • Control 2 reads configuration only — it reports what a machine is set up to do, not what it actually did on the wire. Control 1 is the inverse (behavior, not intent). Neither alone covers both.
  • The post's argument for running both rests on the republished @bitwarden/cli case: it executed on the developer machine doing the install, stole GitHub tokens, and used them to inject malicious CI workflows — so securing CI's registry while leaving developer machines open leaves the attack path open. (This is StepSecurity describing its own product controls.)

Techniques named here

  • Malicious package publication to public registries
  • Credential theft via compromised package install
  • CI pipeline compromise via stolen tokens
  • Hiding malicious code in published wheels
  • Registry fallback to public default

Threat actors named here