lattice-scan
Looks up CVEs that CISA lists as exploited, through the Lattice MCP server, and compares each with what is installed on this machine: packages, snaps, running container images, the kernel. It prints the evidence, including installed version, CISA's dates and required action, and NVD's affected version ranges. A person reads it and decides. It does not detect incidents or decide whether the host is affected, and it never closes a finding by itself.
Standard library only. Python 3.9 or newer.
Install
pipx install lattice-scan # or: pip install lattice-scan
On Debian and Ubuntu, from the signed apt repository:
sudo curl -fsSL https://namiq.io/apt/lattice-scan.gpg -o /usr/share/keyrings/lattice-scan.gpg
echo "deb [signed-by=/usr/share/keyrings/lattice-scan.gpg] https://namiq.io/apt stable main" | sudo tee /etc/apt/sources.list.d/lattice-scan.list
sudo apt update && sudo apt install lattice-scan
The signing key's fingerprint is C473 6154 4115 8A3E 775C 2EC2 7E61 15F4 0A41 061D. Check it against the key you downloaded
(gpg --show-keys /usr/share/keyrings/lattice-scan.gpg). It is also published at https://namiq.io/lattice/docs, apart from the repository.
Or take the single-file lattice-scan.pyz or the .deb from the releases page and check it with
sha256sum -c SHA256SUMS: python3 lattice-scan.pyz --cve CVE-....
Use
lattice-scan --cve CVE-2021-44228 --cve CVE-2026-46300 # works without a key
LATTICE_API_KEY=... lattice-scan --discover # a key lists what was added in the last week
lattice-scan --refresh # collect and print the inventory; no network call
lattice-scan list | impact CVE-... | resolve CVE-... --status fixed --proof "..."
lattice-scan --progress --cve CVE-... # which CVE, and which part of the host or project, as it runs (on by default; -q silences)
lattice-scan --debug --cve CVE-... # every request, response and match decision
Check a project's dependencies
Not sure whether you want this command line, the Claude skill or the Lattice MCP server? The channel guide compares them, lists what works without a key, and has a CI recipe.
Who can scan what. Reading many ids is for a key whose owner has confirmed their email. With a confirmed key, --kev-catalogue lets CISA's catalogue choose the ids. A key whose email is not
confirmed yet (tier trial) is refused with a message to confirm it; no id is read. Without a key you name the CVEs yourself, comma separated:
lattice-scan project . --cve CVE-2026-46300,CVE-2021-44228 (at most 40 a run, inside the keyless daily allowance).
lattice-scan project . --cve CVE-2026-46300,CVE-2021-44228 # no key needed: only the CVEs you name
LATTICE_API_KEY=... lattice-scan project . --kev-catalogue known_exploited_vulnerabilities.json # confirmed key: CISA's file chooses the ids
lattice-scan project app/ api/ --name APP --name API --cve CVE-2026-46300 # explicit ids; several folders at once, labelled
lattice-scan project . --cve CVE-2026-46300 --csv findings.csv # the report on screen, a CSV alongside
lattice-scan project . --cve CVE-2026-46300 --fail-on in-range # exit 1 when a declared version is inside an affected range
Reads what the project declares. Nothing is executed and nothing is installed.
| Ecosystem | Files |
|---|---|
| Python | requirements*.txt and requirements*.lock (with -r includes), pyproject.toml (PEP 621, optional groups, poetry) |
| JavaScript | package.json, package-lock.json |
| Java | pom.xml (properties resolved), build.gradle, build.gradle.kts, gradle.lockfile, gradle/libs.versions.toml |
| Rust | Cargo.toml, Cargo.lock |
| C and C++ | CMakeLists.txt and *.cmake (find_package, FetchContent_Declare, ExternalProject_Add, CPMAddPackage, pkg_check_modules), conanfile.txt / conanfile.py, vcpkg.json |
| Make | Makefile, GNUmakefile, *.mk (NAME_VERSION = x.y.z, pkg-config constraints, library names from -l flags, tarball names in download commands) |
| Containers | Dockerfile* (FROM, pip install, apt-get install, tarball names), docker-compose / compose files (image:) |
- A pinned version (
==2.14.0, a lockfile entry,=1.4.2in Cargo, a Docker tag with two or more numbers such as16.2or13.0.1-security-01) is compared with NVD's ranges. A range (>=1.0), a caret requirement,find_package(X 1.1)(a minimum), a branch name,latestor a major-only tag (16) has no version, and the answer for it isundetermined, neveroutside-range. - A
-lssllink flag or an unversionedfind_packagenames a library, not a version: it is listed so a person knows the library is used. - Where a lockfile pins a name, the unpinned declarations of that name are left out: the lock says what is installed.
- Matching is by identity: the product must be the package (or its npm scope, or vendor+product). For Maven, Cargo, Conan, vcpkg, CMake and Make names the product as a whole token of a longer
coordinate also matches (
org.apache.logging.log4j:log4j-coreis Log4j2), and its versions are compared. A match through an npm scope alone (@vitejs/plugin-reactfor Vite) is a lead only: that package's version is not the product's, so no range is compared.miniothe Python client matches MinIO the server by name; reading the finding tells you which it is. A match is a candidate for a person to read against what is really deployed (a distribution may backport a fix; a lockfile may not be what runs). - Writes no host records (
~/.lattice-scanis untouched). Skipsnode_modules, virtualenvs, build output and.claude. If its file search reaches its limit it says so. - Without a key, Lattice allows 60 reads a day;
--max-read(default 40 without a key, 200 with one) stops a run from using them all, and the ids not read are listed.
Reading the report
The default output is a report for a person: a summary table (one row per project, a total for several), then each project's findings with the declared component and its
file and line, CISA's dates ("listed 3 days ago, due in 18 days"), the version check and CISA's required action, then the ids that were not read, and a legend. Colour is used only on a
terminal (--color auto; never when NO_COLOR is set). --width N fixes the width.
| Verdict | Meaning |
|---|---|
in-range |
A declared, pinned version lies inside a range NVD lists as affected. Check what is really deployed. |
undetermined |
The declaration is a range, a minimum, a floating tag or has no version; NVD's bounds are dates or build ids. |
lead |
The declared package only shares a scope or name with the product, or is its client library (the PyPI minio is not the MinIO server). No range is compared. |
outside-range |
Every pinned version declared is outside every usable range. This does not close a finding. |
not-read |
An id that matches a dependency by name was not read from Lattice (a limit or a stop). Neither safe nor affected. |
For scripts: CSV and JSON
--format csv prints (and --csv FILE writes) one row per finding and declared component, UTF-8, LF line ends, always with a header row, in this fixed column order:
project,cve,title,verdict,component,ecosystem,declared,version,file,line,listed,due,due_status,action,why
version is the pinned version alone (empty when there is none), declared is the text as written (==2.14.0). Text from outside (a CVE title, CISA's action, a file name) that starts with =, +, - or @ is
written with one leading apostrophe so a spreadsheet does not run it as a formula; strip one leading ' for the raw text. --format json (or --json FILE) is the same data with the files read and the ids not read.
lattice-scan project . --cve CVE-2026-46300,CVE-2021-44228 --format csv | \
python3 -c 'import csv,sys; [print(r["cve"], r["component"]) for r in csv.DictReader(sys.stdin) if r["verdict"]=="in-range"]'
Exit status: 0 done, 1 --fail-on matched, 2 bad usage or an unreadable input, 3 stopped part-way (Lattice's allowance ran out or it could not be reached; the ids not read are listed).
The full manual is man lattice-scan (installed by the .deb; man -l man/lattice-scan.1 from a checkout), and lattice-scan project --help has examples and the CSV columns.
Use it as a Claude Code skill
The same tool ships as a plugin whose skill runs it on the machine Claude is running on and reports each match as a candidate for a person to check.
/plugin install lattice-scan --marketplace namiq-ai/lattice-scan
The skill refuses to scan a hosted sandbox, because it would scan the sandbox and not the machine you mean.
What leaves the machine
CVE ids, and the key if you set one. The inventory (package names and versions) is never sent; matching happens locally.
What it writes
~/.lattice-scan/ (or LATTICE_SCAN_DIR; records from the earlier internal name ~/.host-scan are still found), mode 0700: findings, scan, inventory and debug logs. They name this host's installed software. Do not commit,
publish or share them.
What it cannot tell you
- Version ranges are compared as upstream numbers. A distribution can fix a CVE under an unchanged version number, so
in-rangeis a lead, not a finding; the report also shows whether an installed package's own changelog names the CVE (Debian-family hosts only). - Without a key, only
graph_lookupis available and the daily allowance is small. The script waits between calls to stay under it. Getting a key is described at https://namiq.io/lattice/docs#agents; a person does that, and the tool never requests one. - Tested on Debian-family hosts. On rpm and apk hosts the inventory is collected (checked on Fedora and Alpine) but matching and the changelog check are not tested there; the changelog check is Debian-only.
Reporting a problem
A wrong match, a crash, or anything that looks like a security problem: open an issue, or write to contact@namiq.io for anything you would rather not post
publicly. See SECURITY.md.
Licence
Apache License 2.0. See LICENSE and NOTICE. Copyright 2026 NamiQ LLC.
The licence does not grant the right to use the names "NamiQ" or "Lattice" or their logos. Releases up to v0.1.0 were published under MIT; see NOTICE.
Metadata
Release files for lattice-scan 0.2.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| lattice_scan-0.2.2-py3-none-any.whl | Python 3 | none | any | Details for lattice_scan-0.2.2-py3-none-any.whl |
Release files / lattice_scan-0.2.2-py3-none-any.whl
| Download URL | lattice_scan-0.2.2-py3-none-any.whl |
|---|---|
| Size | 48.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4c31f0cb8574ca04a87e657b2431362df4f138bb51a944400eace0abb58a0a23
|
|
BLAKE2b-256 checksum How to use checksums |
5b682930dc03be7f58559c76b4db348390d66b0d286837f2e76735f21cfe61a8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.
Transparency logSecurity for lattice-scan 0.2.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 1 file in this release has been uploaded with provenance metadata.
Attestation for lattice_scan-0.2.2-py3-none-any.whl
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.