Skip to content

Package registry security

Last updated View as MarkdownAgent setup

Gateway can detect software package downloads across seven major package ecosystems and give you policy control over supply chain traffic. When a developer or CI/CD pipeline downloads a package through Gateway, the proxy identifies the registry protocol from the request URL and extracts the package ecosystem, name, version, and namespace. You can then write HTTP policies using pkg.* selectors to allow or block package downloads at whatever granularity you need, from blocking an entire ecosystem to restricting a single package version.

How it works

Package detection operates entirely on the HTTP request URL structure. Gateway recognizes the API contract of each supported registry protocol, not the hostname serving the traffic. This means detection works the same way whether traffic goes to the official public registry, a self-hosted mirror, a corporate proxy registry such as Artifactory or Nexus, or any other compatible endpoint. If the URL path matches the shape of a known registry download, Gateway classifies it.

Detection is fail-open. If the URL cannot be classified as a package download, the request proceeds as normal non-package traffic. A classification failure will never block or break a package install.

Supported ecosystems

Gateway detects package downloads for the following ecosystems:

Ecosystem Example artifact pattern Namespace
npm /{package}/-/{package}-{version}.tgz Scope (for example, @babel)
PyPI /packages/{hash}/{hash}/{hash}/{package}-{version}.whl
RubyGems /gems/{package}-{version}.gem
Cargo /crates/{package}/{package}-{version}.crate
Go /{module}/@v/{version}.zip Module path
Maven /maven2/{group}/{artifact}/{version}/{artifact}-{version}.jar Group ID
NuGet /{package}/{version}/{package}.{version}.nupkg

Detected operation

The initial release detects download operations only. Download is the one operation where the package name, version, and ecosystem are all derivable from the URL path across every supported registry. Other operations such as resolve (metadata lookups) and publish use different endpoints, hosts, or HTTP methods that require additional signal beyond the URL path.

Selectors

The following selectors are available for HTTP policies with the Allow and Block actions:

Selector UI name API example Description
pkg.ecosystem Package Ecosystem pkg.ecosystem == "npm" The package ecosystem detected from the request URL.
pkg.name Package Name pkg.name == "lodash" The package name extracted from the download URL.
pkg.version Package Version pkg.version == "4.17.21" The package version extracted from the download URL. Supports exact match and ecosystem-aware comparison operators.
pkg.namespace Package Namespace pkg.namespace == "@babel" The package namespace, when the ecosystem supports one. For npm this is the scope, for Maven this is the group ID, and for Go this is the module path.
pkg.purl Package URL pkg.purl == "pkg:npm/lodash@4.17.21" The Package URL (PURL) derived from the detected coordinates. Available in the API only.

Build expressions in the dashboard

In the dashboard, Package Ecosystem is the primary selector. Selecting it reveals nested fields for specifying package name, version, and namespace. The following rules apply:

  • You must select an ecosystem before any other package fields become available.
  • You must select exactly one ecosystem to enable the nested fields. If you use the in or not in operator to match multiple ecosystems, the nested package name, version, and namespace fields are disabled.
  • You must add a package name before you can add a version constraint.
  • The namespace field is only available for ecosystems that support one. The label changes based on the selected ecosystem: Scope for npm, Group ID for Maven, and Module namespace for Go.
  • The pkg.purl (Package URL) selector is not available in the dashboard. Use the API to write expressions that match on PURL.

When using the API directly, these selectors can be combined freely in wirefilter expressions without these constraints.

Version comparison operators

The pkg.version selector supports ecosystem-aware comparison operators in addition to exact string matching. Each ecosystem uses its own native versioning semantics:

Ecosystem Versioning standard
npm SemVer
Cargo SemVer
PyPI PEP 440
RubyGems Gem::Version
Go Go module versions
Maven Maven version ordering
NuGet NuGet normalization and ordering

The following comparison operators are supported:

Operator API syntax Description
equals == Normalized equality using ecosystem-specific identity rules.
not equals != Negation of normalized equality.
greater than > Version is greater than the specified value using native ordering.
greater than or equal >= Version is greater than or equal to the specified value.
less than < Version is less than the specified value.
less than or equal <= Version is less than or equal to the specified value.

When a version string cannot be parsed by the ecosystem's versioning rules, or when the detected ecosystem does not match the comparison context, the comparison returns no match. This includes !=, meaning an unparseable version does not match anything.

Example policies

Block all downloads from a specific ecosystem

To block all PyPI package downloads across your organization:

Selector Operator Value Action
Package Ecosystem is pypi Block

Wirefilter expression:

pkg.ecosystem == "pypi"

Block a specific package

To block a known malicious or unwanted npm package regardless of version:

Selector Operator Value Logic Action
Package Ecosystem is npm And Block
Package Name is event-stream

Wirefilter expression:

pkg.ecosystem == "npm" and pkg.name == "event-stream"

Block vulnerable versions of a package

To block all versions of lodash below 4.17.21, which is the version that patched CVE-2021-23337:

Selector Operator Value Logic Action
Package Ecosystem is npm And Block
Package Name is lodash And
Package Version less than 4.17.21

Wirefilter expression:

pkg.ecosystem == "npm" and pkg.name == "lodash" and pkg.version < "4.17.21"

Restrict packages to a sanctioned registry mirror

To allow npm package downloads only through your corporate Artifactory instance and block all other npm downloads, create two policies:

Policy 1 - Allow sanctioned mirror (higher priority):

Selector Operator Value Logic Action
Package Ecosystem is npm And Allow
Host is npm.internal.example.com

Wirefilter expression:

pkg.ecosystem == "npm" and http.request.host == "npm.internal.example.com"

Policy 2 - Block all other npm downloads (lower priority):

Selector Operator Value Action
Package Ecosystem is npm Block

Wirefilter expression:

pkg.ecosystem == "npm"

Because detection is based on the registry protocol rather than the hostname, both the public registry.npmjs.org and your internal mirror at npm.internal.example.com are detected as npm traffic. Policy 1 (at higher priority) allows the sanctioned mirror, and Policy 2 blocks everything else.

Block a specific package unless from a sanctioned host

To allow downloads of a sensitive internal package only through your corporate registry, blocking it from all other sources:

Policy 1 - Allow from sanctioned host (higher priority):

Selector Operator Value Logic Action
Package Ecosystem is npm And Allow
Package Namespace is @acme And
Package Name is internal-sdk And
Host is npm.internal.example.com

Wirefilter expression:

pkg.ecosystem == "npm" and pkg.namespace == "@acme" and pkg.name == "internal-sdk" and http.request.host == "npm.internal.example.com"

Policy 2 - Block from all other hosts (lower priority):

Selector Operator Value Logic Action
Package Ecosystem is npm And Block
Package Namespace is @acme And
Package Name is internal-sdk

Wirefilter expression:

pkg.ecosystem == "npm" and pkg.namespace == "@acme" and pkg.name == "internal-sdk"

Detection and mirrors

Package detection classifies traffic based on the URL path structure of each registry's download API. It does not rely on matching against a list of known registry hostnames. This design means that any server serving packages using a compatible URL layout is detected the same way, whether it is:

  • The official public registry (for example, registry.npmjs.org or pypi.org)
  • A corporate proxy registry such as JFrog Artifactory, Sonatype Nexus, or AWS CodeArtifact
  • A self-hosted mirror
  • A CDN-fronted registry endpoint

The http.request.host selector remains available for policies that need to distinguish between specific registry hosts. By combining pkg.* selectors with http.request.host, you can write rules that apply different actions depending on where the package is being fetched from.

Logging

When Gateway detects a package download, the package metadata is included in the Gateway HTTP log. This data is available in Gateway activity logs and through Logpush.

Activity Log fields

The following package fields are available in the Gateway Activity Log:

Field Description
Package Ecosystem The detected registry type (for example, npm, pypi, cargo).
Package URL (PURL) The Package URL string derived from the detected coordinates (for example, pkg:npm/lodash@4.17.21).

Logpush fields

Package metadata is available in the PackageInfo object in the gateway_http Logpush dataset:

Field Type Description
PackageInfo.Ecosystem string The detected package ecosystem.
PackageInfo.Namespace string The package namespace, if applicable.
PackageInfo.Name string The package name.
PackageInfo.Version string The package version string.
PackageInfo.Purl string The Package URL string.

Limitations

  • Package detection requires TLS decryption to be turned on. Packages downloaded over connections that bypass TLS inspection (due to Do Not Inspect policies or applications on the inspection limitations list) are not detected.
  • Some language runtimes and HTTP clients maintain their own certificate trust stores separate from the operating system. If the certificate used for inspection (such as the Cloudflare managed certificate) is only installed in the OS trust store, package downloads from these clients may fail with certificate verification errors. Refer to Install certificate manually for instructions on adding the certificate to application-specific trust stores, including Python, Node.js, Ruby, Rust, Java, and Go.
  • Only download operations are detected. Metadata lookups (resolve), package publishing, and other registry operations are not classified.
  • Version comparison uses each ecosystem's native ordering rules. Cross-ecosystem version comparisons are not supported.
  • Ecosystem-specific range syntax (such as npm ^1.2.3, PyPI ~=1.4, or Maven interval notation) is not supported. Use the individual comparison operators (>, <, >=, <=) instead.

Was this helpful?