Quarantine for your dependencies.
Lazaret provides static security analysis for Python, JavaScript, TypeScript and SQL, plus supply-chain and credential checks for Go and Rust. It also inspects packages from npm, PyPI, Go and Rust, editor extensions, and every install before it runs—flagging hidden payloads, leaked secrets, injection bugs and known-vulnerable versions.
pip install lazaret
Python 3.10 or later. Nothing else gets installed.
The project scanner is on npm too, with no dependencies: npx lazaret check . (Node 22 or later). The Python package includes both the scanner and MCP server; npm provides the scanner and commit-time hook but no MCP server in 0.1.10. You do not need both packages. Full MCP capability from either package is planned for 0.2.0.
A lazaret was the harbor station where arriving ships and cargo were held and inspected before they could enter port. Ships in quarantine flew a yellow flag.
| Package | Verdict | Finding |
|---|---|---|
| npm:chalk@6.0.1 | OK | |
| npm:react-zutils@1.0.1 | SUSPICIOUS | SC-INSTALL-HOOK its install hook starts a script that contacts an ngrok address, hidden in strings it XOR-decodes as it runs |
| pypi:requests@2.34.2 | OK | |
| pypi:requesxs@1.0.0 | SUSPICIOUS | SC-EVAL-DECODE runs a payload it decodes SC-TYPOSQUAT one letter from “requests” |
In plain English
If you run technology rather than write code, this part is for you.
Your software is mostly other people's software
Almost every application your teams build is assembled from hundreds of free, shared building blocks, called packages, that are downloaded automatically from public warehouses such as npm (for JavaScript) and PyPI (for Python). It is a restaurant kitchen that orders its ingredients from thousands of suppliers it has never met.
Attackers poison the ingredients
Criminals publish packages named one typo away from popular ones, or break into a real publisher's account and slip a booby trap into an update. When a developer's laptop or your build server downloads it, the trap runs there, with that person's passwords and access, usually before anyone has looked at it.
Lazaret is the inspection dock
Before a package is installed, Lazaret opens it and reads what its code would do. Would it send out passwords? Run hidden code? Delete files? Then it gives a verdict, and the install tool it sits in front of (npm, pip, go, cargo and others) stops on the bad ones, as the harbor did with a ship.
- OKNothing unusual found.
- WARNSomething unusual that a person should know about. The install goes ahead.
- SUSPICIOUSIt behaves like an attack. The install is held.
- INCOMPLETEPart of it could not be read, so it is not waved through.
It reads; it never runs
Nothing in a package is installed or executed during inspection. Lazaret reads the files in memory, so inspecting a booby-trapped package cannot set the trap off.
What your team does
Put lazaret guard in front of the install command developers already use, for example lazaret guard npm install express. Run a scan in your build pipeline. Add the commit hook if you want to catch a leaked password before it is saved. It is free and open source, needs no account, and has no dependencies of its own.
Questions leaders ask
We already run a vulnerability scanner. Isn't that this?
Not quite. Tools such as npm audit, Dependabot and pip-audit compare your packages with lists of problems that someone has already found and published. A package that was poisoned this morning is on no list yet. Lazaret reads the package itself to see what it does, and it also does the list check, so you get both.
Will it block our developers for no reason?
That is the cost we watch most closely. Before every release we run it over the latest versions of 1,703 of the most downloaded packages, and none may be held. Version 0.1.9 holds none of them. Version 0.1.8 held nine by mistake, among them vite and vitest, and that was fixed. What it still warns about, and why.
How much does it catch?
On the canonical audit draw from an open dataset, it holds 454 of 516 selected malicious releases (88.0%), or 454 of the 513 that contain files to scan (88.5%). Counting releases flagged for review, it finds 478 of 516 selected (92.6%), or 478 of 513 scanned (93.2%). On 750 others that no detector was written from, the figures are 82% and 89%. No scanner catches everything: three selected releases in the canonical draw are empty, and some attacks fetch their payload after install, from outside the package. Treat Lazaret as one layer of defense, not the only one.
Does it send our code anywhere?
Scanning your code happens on your own machine. It contacts the public registries to download the packages you ask it to inspect. One feature talks to outside services, and only when you ask for it: --verify-secrets checks whether a leaked key is still live by asking the service that issued it, and says which providers it will ask before it does.
What it checks
- Your code
- Rules for Python, JavaScript, TypeScript and SQL, with taint tracking that follows untrusted input across functions and files into SQL, shell and template sinks, and Flask, Django, FastAPI and Express routes modeled. Go and Rust source is checked for credentials, token formats, Trojan Source characters, and hidden or encoded content. Reports as HTML, JSON, or SARIF for code scanning, with a quality gate for CI.
lazaret .- Your installed dependencies
- Follows each install hook in
node_modulesandsite-packagesto the scripts it runs, and tests every file for what no library needs to do: sending credentials out, running what it downloads or decodes, starting itself at login, deleting the user's files. Vendored Go and Rust trees are read too. lazaret . --deps- Published packages
- Downloads npm, PyPI, Go module and crate archives, and VS Code extensions, scans them in memory without unpacking anything to disk, and gives each version a verdict. Reads what runs at install, at import and when the package is used, the strings it decodes as it runs, a value one file receives and another runs, who built a release and from which repository, and names one typo away from a popular package's.
lazaret-registry scan npm:left-pad- Installs, before they run
- Put
lazaret guardin front of npm, pnpm, yarn, Bun, pip, uv, go, cargo or your editor's extension installer: it resolves what would be installed, scans every package in memory, and installs nothing if one is suspicious, can't be checked, or is less than two days old. lazaret guard npm install express- Secrets, and whether they are live
- Finds credentials in code and config files, and, only when asked, checks with the issuing service whether a leaked key still works. The commit-time gate stops a credential, or an install hook that pipes a download into a shell, before it is committed.
lazaret hook- Known vulnerabilities
- Matches the npm, PyPI, Go and Rust packages installed or pinned in a project, from eight lockfile formats including uv.lock, pylock.toml and bun.lock, against a CVE bundle built from OSV, CISA KEV and EPSS, and fails the build on versions with known-exploited vulnerabilities.
lazaret-sca . --bundle cve-bundle.json- Code your AI assistant writes
- The Python package's MCP server lets Claude and other assistants scan a snippet before writing it to disk, or check the files they just changed.
pip install lazaretsupplies both this server and the project scanner; an npm installation is not also required. Installing only the npm package does not supply MCP in 0.1.10. Instruction files that tell an agent what to run are read for what they ask. lazaret-mcp
Measured on real malware
Lazaret 0.1.9 and 0.1.8 were run on the same canonical audit draw, read statically: nothing was installed or run. Other tools' earlier measurements are labelled separately.
| What was measured | Lazaret 0.1.9 | Comparison |
|---|---|---|
| Malicious npm and PyPI releases held at the strictest verdict (516 selected; 513 scannable and three empty releases counted as misses) | 88.0% selected 88.5% scanned | 0.1.8: 87.0% selected, 87.5% scanned GuardDog: 71% (September, on an earlier sample) |
| Malicious releases held or flagged for review (the same canonical draw) | 92.6% selected 93.2% scanned | 0.1.8: 91.1% selected, 91.6% scanned GuardDog: 77% (September, on an earlier sample) |
| Other malicious releases that no detector was written from, held (750) | 82.4% | 0.1.8: 81.9% Other tools: not run |
| Popular releases held by mistake (1,703 of the most downloaded npm and PyPI packages and Rust crates) | 0 | 0.1.8: 9 GuardDog: 18 of September's 429 |
| Malicious npm releases blocked at install, end to end (300) † | 247 | Popular npm packages blocked by mistake: none of 219 |
| Planted fake credentials found (99, of 33 types) † | 95% | Gitleaks 94%, detect-secrets 91%, TruffleHog 73% |
| Expected advisories matched in eight lockfile formats (1,278) † | 100% | pip-audit and npm audit each miss one |
† Last measured on October 10, on an earlier build of 0.1.9, and not repeated for the release.
The canonical draw selected 516 releases. Three contain no files—bolym 1.0, detectron2 0.6 and requestsx 0.1.2—so 513 artifacts were scanned and the empty releases count as misses in the selected denominator. Lazaret 0.1.8 and 0.1.9 scanned the same 513 IDs; eleven verdicts moved upward and none moved downward. The 750-release holdout was not read while detectors were written. GuardDog's figures are from September's earlier sample, and much of the dataset was found by GuardDog's own rules, which favors it. How it was measured, and the limits, are in the README.
What it says about popular software
Popular packages are almost never malware, but their code can look like an attack's: a loader that fetches a script and runs it, a client that sends a login token, a tool that starts itself every time Python does, a compiled program inside a source package. Lazaret reports that. Before each release it reads the latest versions of 1,703 of the most downloaded npm and PyPI packages and Rust crates, and none may be held.
- 0held (SUSPICIOUS)
- 44warned
- 2not readable in full
- 1,657OK
A warning says what the code does and leaves the decision to you. A hold is for code that does something no library needs. Version 0.1.8 held nine popular releases by mistake, and its install guard blocks what it holds by default, so installing them would have failed. These are the nine, and what 0.1.9 says about the same releases:
| Package | What its code does | 0.1.8 | 0.1.9 |
|---|---|---|---|
| vite 8.3.2 | A development server. It evaluates text it has decoded, and calls its own address from a bundle that reads the environment. | SUSPICIOUS | WARN |
| vitest 5.0.3 | A test runner. It runs code it is given inside Node's virtual machine. | SUSPICIOUS | OK |
| monaco-editor 0.57.0 | A code editor for web pages. Its loader fetches a script from an address and runs it. | SUSPICIOUS | OK |
| coverage 7.16.2 | A test-coverage tool. A startup file runs code each time Python starts, when asked to measure. | SUSPICIOUS | WARN |
| numba 0.68.0 | A speed-up compiler for Python. It reads its own source back and runs it. | SUSPICIOUS | OK |
| future 1.0.0 | A Python 2 and 3 compatibility library. Its network code sends proxy credentials, as a network library must. | SUSPICIOUS | OK |
| sympy 1.14.0 | A maths library. It runs an expression it builds, from a list it reverses. | SUSPICIOUS | OK |
| ipython 9.17.1 | An interactive Python shell. Its tab-completion evaluates a list it reverses. | SUSPICIOUS | OK |
| kubernetes 36.0.3 | A client for Kubernetes clusters. It sends its service-account token to the cluster's API, which is its job. | SUSPICIOUS | OK |
The 44 warnings are for things such as programs and prebuilt libraries shipped inside a package, data that decodes to something runnable, and scripts that run at install. The two releases it cannot read in full are a library of compressed model files and a 16 MB native library. Beyond this list, two Go modules in Ubuntu's archive are still held by a known false alarm in our Go reader, which we will fix after 0.1.9. We list them rather than hide them.
New in 0.1.9
- Go modules, Rust crates and VS Code extensions get the same inspection as npm and PyPI packages, and
lazaret guardnow sits in front ofgo,cargoand your editor's installer as well. - No popular release is held by mistake: none of 1,703, where 0.1.8 held nine.
- A repository can be scanned at a given commit (
lazaret scan github:owner/repo), andlazaret hookchecks files as they are committed. --verify-secretsasks the service that issued a leaked key whether it is still live. It is opt-in, and it says who it will ask first.- New things it recognizes: code that deletes the user's files, instruction files that tell an AI agent what to run, workflows that send secrets out, look-alike packages from look-alike accounts, version numbers inflated to outrank a private package, and a release that suddenly brings risky behavior its predecessor did not have.
- Registry traffic goes through Lazaret's own HTTPS client, written without third-party code, and a release's provenance (who built it, from which repository) is checked and reported.
- Tar and zip archives are read more strictly and within memory limits, and proxy and redirect handling is tighter.
- Native packages for Alpine and other musl Linux systems, and for Windows on ARM.
Nothing to trust but the standard library
A supply-chain tool with dependencies is itself a supply-chain risk. Lazaret has none. It runs on Python's standard library alone, builds without downloading anything, and its whole test suite runs on a stock interpreter. The npm package has no dependencies either, and the native engine is written with no external crates: even its regular-expression engine is its own, CPython's ported to Rust, with shortcuts that skip only work that can't match. So is its HTTPS client.
Releases are built reproducibly and published from CI with signed attestations, so any file on PyPI or npm can be traced to the exact commit and workflow that produced it. This page loads no scripts, fonts, or anything else from third parties, and its content security policy holds it to that.