Metadata filtering
Remove blocked versions from registry listings, so package managers quietly pick a safe version instead of failing at download.
Without metadata filtering, a package manager can choose a blocked version and only find out when the download returns 403. With it, the firewall checks the versions listed in each package's metadata and removes the blocked ones before the client sees them. A range like ^3.0.0 then resolves to the newest allowed version, and the install succeeds.
| Without filtering | With filtering | |
|---|---|---|
npm install pkg@^3 when 3.0.1 is blocked | Picks 3.0.1, fails with E403 | Picks 3.0.0, succeeds |
npm install [email protected] | E403 | notarget: the version doesn't exist as far as npm can tell |
npm install pkg when latest is blocked | E403 | latest is rewritten to the newest allowed version, so it succeeds |
npm ci from a lockfile with 3.0.1 | E403 | E403 (lockfiles skip metadata) |
Downloads are still checked either way. Filtering only changes what the listing shows.
Before you start:
- A running firewall (Block installs with Firewall)
- Redis cache set up. Filtering works without it, but Redis is recommended: it keeps filtering verdicts across restarts and shares them between instances.
1. Turn it on
Append to .env:
METADATA_FILTERING_ENABLED=trueThe defaults are right for most deployments: blocked versions are removed, warned versions stay visible, and the newest 100 versions of each package are checked. To change them, see Settings worth knowing.
docker compose up -d --force-recreateTo set it in socket.yml instead, add a top-level block (a sibling of socket:, not inside it):
metadata_filtering:
enabled: true2. Create something to filter
You need a package with one blocked version. Add a deny rule for is-odd version 3.0.1 to your Default policy, as shown in Policy config: Block or allow one package.
Verify
Compare the version list from the public registry with the list through the firewall:
diff <(curl -s https://registry.npmjs.org/is-odd | jq -r '.versions | keys[]') \
<(curl -sk https://sfw.example.com/npm/is-odd | jq -r '.versions | keys[]')Expected: the blocked version is the only difference.
7d6
< 3.0.1
The firewall logs the same result:
docker compose logs socket-firewall | grep "metadata filtering complete"
# npm metadata filtering complete: 6 allowed, 1 filtered out of 7Then see what npm does with it, from a fresh directory:
cd "$(mktemp -d)" && npm cache clean --force
npm install is-odd@^3.0.0 # succeeds, installs 3.0.0
npm ls is-odd
npm install [email protected] # npm error notarget No matching version found for [email protected]- The
diffshows only3.0.1, and the log shows1 filtered -
npm install is-odd@^3.0.0succeeds and installs3.0.0 -
npm install [email protected]fails withnotarget, not E403 - Removing the deny rule (and waiting out the cache, below) brings
3.0.1back
If the diff is empty, check that the firewall was recreated after the .env edit and that the deny rule is in the Default policy. If anything requested is-odd metadata before you added the rule (for example, a baseline diff), its allowed verdicts are cached: flush Redis (rc FLUSHDB, see Redis cache) and run the diff again.
How each ecosystem is filtered
| Granularity | Ecosystems | What's removed |
|---|---|---|
| Per version | npm, Go, RubyGems, Cargo, Conda, OpenVSX, NuGet (registration and flat-container) | The whole version |
| Per file | PyPI | Individual files (one wheel or the sdist). A version-level block removes all of its files |
| Per version in listings, per file at download | Maven | Blocked versions are removed from maven-metadata.xml. Downloads are checked per file (a blocked version's .jar returns 403 even if its .pom is allowed) |
Settings worth knowing
Set these in socket.yml, under a top-level metadata_filtering: block, then recreate the container.
metadata_filtering:
enabled: true
filter_warn: false
max_versions: 100
include_unchecked_versions: true
cache_ttl: 3600| Setting | Default | When to change it |
|---|---|---|
filter_warn | false | true also hides versions with a Warn action. See the caution below |
max_versions | 100 | Newest N versions checked per package. Raise it if teams install old versions of very active packages |
include_unchecked_versions | true | Leave it on. See the warning below |
cache_ttl | 3600 | How long per-version filtering verdicts are cached, in seconds |
Usesocket.ymlfor these, not.envOn firewall 2.16.0, only
METADATA_FILTERING_ENABLEDis read from the environment.METADATA_FILTER_WARN,METADATA_MAX_VERSIONS,METADATA_INCLUDE_UNCHECKED_VERSIONSandMETADATA_CACHE_TTLare listed in the Configuration Reference but ignored.
filter_warn: truecan empty a listingIt hides every version with any warn alert. Common warn alerts such as Unmaintained, Deprecated and Unpopular can apply to every version of a package, which then disappears entirely and every install of it fails. Check your Warn rules before turning this on.
include_unchecked_versions: falsedoes not mean "hide unscanned versions"It hides every version outside the
max_versionswindow. On packages with frequent prereleases, the newest 100 versions can all be prereleases, sofalsecan remove every stable version from the listing. To block versions Socket hasn't scanned yet, setsocket.fail_open_unscanned: false(orSOCKET_FAIL_OPEN_UNSCANNED=false), which hides them from listings and blocks their download.
Why filtering seems not to work
| Situation | What happens |
|---|---|
Lockfile or pinned version (npm ci, pip install -r with == pins, fixed Maven versions) | The client goes straight to the download. It's still blocked, but no fallback version can be chosen |
| Client metadata cache | The client reuses old metadata. Clear it once: npm cache clean --force, pip cache purge, delete ~/.m2/repository/<group> |
| Policy just changed | Filtering verdicts are cached for up to cache_ttl (1 hour). Flush Redis to apply now (see Redis cache) |
Blocked version is older than the newest max_versions | It isn't checked, so it stays listed. Its download is still blocked |
| No allowed version matches the range | The install fails with notarget instead of E403 |
Event volume
Filtering checks each listed version with Socket, and each check is logged as a decision event with event_category: "proactive". One listing can produce up to max_versions events (about 100 for a busy package like rollup). A version removed from a listing is logged with decision: "blocked" and response_code: 403, even though the client received a normal 200 listing.
Expect event volume in logs and your SIEM to rise as soon as filtering is on. To count real install blocks, exclude event_category: "proactive". See Data.
Works with cooldown
With a cooldown set to Block, versions younger than the window are removed from listings too, and latest moves to the newest version past the window. Range installs and bare npm install pkg then succeed with no error. A developer who pins a brand-new version gets notarget.
Performance
The first metadata request for a package checks up to max_versions versions with Socket, so it's slower than an unfiltered request. Later requests are served from the Redis cache. Very large metadata (Conda repodata.json, the most popular npm packages) is buffered in memory, up to max_body_size (500 MB by default).
Reference
Updated 8 days ago