Skip to content

Dozor — advisory-driven blocklists

NORA keeps no advisory database. That is a deliberate architecture decision (ADR-2): the OSV exports for the ecosystems NORA proxies are roughly 360 MB compressed and rotate daily, and matching versions against them needs an index — an embedded database inside a process that starts in three seconds and idles under 50 MB.

Dozor is the companion that fills the gap from outside. It compiles the OSV feed together with an inventory of what your registry actually holds into a blocklist.json in the format NORA already reads. No NORA changes, no plugin, no network between the two — the file is the whole interface.

osv.dev snapshot ─┐
registry inventory ─┼─► dozor build ─► blocklist.json ─► git PR ─► NORA
policy.toml ─┘

Dozor is a compiler, not a service. It has no daemon and never sits on the download path, so it cannot fail a download or hold up a build.

The npm OSV feed carries 228,684 advisories, and 96.8% of them are MAL-* — malicious package reports, not CVEs: typosquats, hijacked maintainers, poisoned releases. They carry no severity field at all, so a policy expressed only as a severity threshold would discard almost all of them.

A CI scanner sees a malicious package only after your registry has already cached it and served it to the team. The registry is the one place where refusing it is prevention rather than detection.

Download a binary from the releases page, or build from source:

Terminal window
git clone https://github.com/getnora-io/dozor
cd dozor && cargo build --release
Terminal window
# 1. snapshot the feed (or carry the zip in, for air-gapped sites)
curl -o npm.zip https://osv-vulnerabilities.storage.googleapis.com/npm/all.zip
# 2. take an inventory of what NORA holds
dozor inventory --nora-data /var/lib/nora -o inventory.jsonl
# 3. compile the blocklist
dozor build \
--feed-npm npm.zip \
--inventory inventory.jsonl \
--policy policy.toml \
--proactive \
-o blocklist.json
# 4. hand it to NORA — this is all the configuration there is
export NORA_CURATION_MODE=enforce
export NORA_CURATION_BLOCKLIST_PATH=/etc/nora/blocklist.json

--proactive also blocks every wholly-malicious package in the feed whether or not you have ever cached it, so the package is refused before its first download.

version = 1
[defaults]
severity_threshold = "high" # critical | high | moderate | low | off
malicious = "block" # MAL-* reports have no severity — own switch
[[exception]]
registry = "npm"
name = "lodash"
version = "4.17.20"
reason = "pinned by a legacy build, tracked in JIRA-42"
expires = "2026-12-31" # required — an expired exception fails the build

The reason string Dozor writes travels through NORA untouched and lands in the 403 body:

{
"error": "blocked_by_policy",
"context": {
"package": "lisa-bubursumsum21-miaww",
"version": "0.0.1-security.1",
"rule": "blocklist",
"reason": "MALICIOUS package (MAL-2025-115501) — blocked by dozor · https://osv.dev/vulnerability/MAL-2025-115501"
}
}

The output is an artifact, not a controller’s opinion:

  • Deterministic — identical inputs give byte-identical output, so the daily job opens a pull request only when something actually changed.
  • Verifiable — every output carries an x-dozor-derivation block with the digests of the feed snapshot, the inventory, the policy and the body itself. dozor verify re-derives it, so you can prove months later why a build was blocked.
  • Reviewable — the blocklist is a file in git, and git log -S <package> answers “when did we start blocking this, and who approved it”.

No operator, no CRD, no reconcile loop: a job that writes a file.

dozor build never reaches the network. Copy the feed zip in by whatever means you already use for artifacts, and the inventory comes from a cold copy of NORA’s data directory. Nothing else is required.

Dozor 0.1 covers npm. Every other ecosystem answers Unknown, and Unknown never becomes “safe” — those versions are counted and sampled in x-dozor-unmatched in the output rather than being silently allowed.

PyPI (PEP 440) is next, followed by Maven, Go and RPM/deb version semantics.