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.
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.
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 |
— |
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.
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. |
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
inornot inoperator 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.
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.
To block all PyPI package downloads across your organization:
| Selector | Operator | Value | Action |
|---|---|---|---|
| Package Ecosystem | is | pypi |
Block |
Wirefilter expression:
pkg.ecosystem == "pypi"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"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"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.
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"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.orgorpypi.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.
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.
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). |
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. |
- 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.