Check a dependency list before it ships.
Upload an SBOM or lockfile. Every package is checked against malware and vulnerability databases, its publishing history, and its install code. Nothing is installed or run.
- CycloneDX
- SPDX
- requirements.txt
- package.json
- package-lock.json
Paste the contents instead
Try a sample
Options
Scanning
0.0sResponse
What to do, in order. Tick items off as you go: progress is saved with the report, so the team sees the same list.
Loading…
Loading…
In Finnish folklore every place has a haltija, a guardian spirit that watches over a house, a sauna or a forest. This one watches over your build.
How a verdict is reached
Every package in the SBOM is checked three ways: against databases of known threats, against its own publishing history, and, when something already looks off, by reading its code. Evidence from the web can raise suspicion but never decides on its own.
The pipeline
A scan parses the file into exact package versions, then gathers four kinds of evidence. Known threats and package signals run for every package; the slower checks only run where there is already a reason to look.
Solid boxes can decide a verdict. The dashed box only adds weight to a package that already shows warning signs. The LLM sits beside the pipeline: it writes summaries and briefs from the same evidence.
Verdicts and the gate
Each package goes through the same questions in the same order. The first "yes" sets its verdict; the verdicts then decide the gate.
The gate
HTTP 409HTTP 200HTTP 200How the package-signal score works
Each warning sign has a weight between 0 and 1: roughly, how likely it is on its own that a package showing it is malicious. Signs are combined so that several weak ones add up, but no pile of weak ones reaches certainty:
Weights for signals that occur often enough in the evaluation data were fitted on a development split, aiming for the most caught malware at a false-positive rate of at most 0.5%. Rare signals keep their hand-set value. Current weights:
Loading…
False-positive guards
- Popular look-alikes are not squats.A name close to a popular package is ignored when the package itself is widely used or long established.
- Age is never enough.New, few releases and fresh release only count alongside a sign that isn't about age.
- Missing repository links only count for long-tail packages,because old versions of popular packages often lack them.
- Code rules ignore comments, docstrings, tests and docs,and credential theft needs reading and sending in the same file.
Code inspection
Downloading every archive would be slow and noisy, so only long-tail packages that already show a warning sign (or have install hooks) are read. Nothing is extracted to disk and nothing is executed.
Ranking vulnerabilities
Severity alone is a poor guide: most critical CVEs are never exploited. Haltija combines how bad a vulnerability is (CVSS) with how likely it is to be exploited in the next 30 days (EPSS), and anything on CISA's list of vulnerabilities already exploited in the wild goes straight to the top.
The upgrade target is the nearest release that fixes every advisory on that package, so one change clears them all.
Evidence from outside the package
These checks look beyond the package itself. Each has a strict rule for what counts, because the web is full of pages that mention a package without being about it.
Source repository
Fetches the manifest of the GitHub repository the package links to. If that repository publishes a different, popular package, the link borrows someone else's credibility ("starjacking").
Counts: declared name differs and is popular. Monorepos and forks don't count.
Web reports
For flagged packages, searches Exa and context.dev for reports.
Counts: the page is about this exact package (name in title or address) and says it's malicious. For popular packages the page must also name the version.
Hosts in the code
Domains and IPs found by code inspection are looked up in stored write-ups and on the web: far more specific than searching a name.
Counts: the page names that exact host near a threat word. Shared platforms (GitHub, Discord, clouds) are skipped.
Security write-ups
Recent articles by supply-chain researchers are read regularly, and the packages and indicators they call malicious are stored.
Counts: the article names the package as malicious, not as the one being imitated. Names not in the text are dropped.
How write-ups are used
The LLM's role
It does
- Write the summary for each flagged package from the collected evidence
- Give a second opinion on likely false positives
- Write incident briefs: what malware takes and what to rotate
- Extract package names and indicators from security write-ups
It never
- Changes a verdict or a score
- Decides the CI gate (the gate doesn't call it at all)
- Adds names that aren't in the source text: extracted names are checked against the article
Response
The response step turns findings into a checklist in order of urgency. Reversible steps (notify, patch) can happen straight away; blocking and removal need confirmed evidence, and deploying past a failed gate needs a recorded reason.
The CI gate
The gate runs the deterministic part of the pipeline only (no web search, no LLM), so the same SBOM always gets the same answer and a flaky provider can't break a build.
Both answers end with a link to the findings page.
Or from a terminal, without a server: haltija check sbom.cdx.json runs the same checks locally and exits 1 when the deploy should be blocked (pip install -e . in the repository registers the command).
Measuring it
Two independent checks keep the numbers honest. The evaluation replays known malware with the malware databases switched off; the live feed scores packages nobody has judged yet and waits to see which turn out malicious.
Evaluation
- Malware: OSV malicious-package advisories for PyPI, split into a development and a test set
- Benign: 1,000 long-tail PyPI packages
- Each package is scored as of its advisory date, so later deletions don't leak the answer
- Weights are fitted on development and reported on test, with 95% confidence intervals
Live feed
Limits
- A compromised release of an established package shows few package signals; diffing it against the previous release would help.
- Reachability isn't checked: a vulnerable function you never call still counts.
- The evaluation set is PyPI only; npm is covered by the same signals but not separately measured.
- Outside evidence is time-dependent, so it isn't part of the evaluation numbers and can only be measured going forward.
- Package signals catch about a third of live malware with no false positives on the test set; the rest needs database reports or outside evidence.