Skip to main content

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 recent                                        # no CVE id? read namiq.io's weekly list of recent exploited/critical CVEs; only likely matches go to Lattice (--daily: last day or two; --open: older ones, overdue first)
printf '...' | lattice-scan enrich                         # ids from an audit or advisory: exploited? since when? patch due? ransomware? (no key: 20 ids a run)
lattice-scan get-key --name ... --position ... --company ... --email ... --purpose "..."   # asks nothing until --accept-terms; for a real person who read the terms
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.2 in Cargo, a Docker tag with two or more numbers such as 16.2 or 13.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, latest or a major-only tag (16) has no version, and the answer for it is undetermined, never outside-range.
  • A -lssl link flag or an unversioned find_package names 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-core is Log4j2), and its versions are compared. A match through an npm scope alone (@vitejs/plugin-react for Vite) is a lead only: that package's version is not the product's, so no range is compared. minio the 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-scan is untouched). Skips node_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.

In OpenAI Codex

Codex reads the same SKILL.md format. The repository carries it where Codex looks: clone it and .agents/skills/lattice-scan is found when you start Codex in the folder. To use it everywhere, copy skills/lattice-scan to ~/.agents/skills/lattice-scan, or ask Codex's skill installer for it from namiq-ai/lattice-scan, path skills/lattice-scan. Each release also attaches lattice-scan-openai-plugin-<version>.zip, the package for OpenAI's plugin directory. In ChatGPT the skill stops at its first step, because ChatGPT runs it in a hosted sandbox and it checks the machine it runs on; Codex on your own computer is where it does its work.

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. recent also downloads namiq.io's public list of recent CVE ids; that request carries no id and nothing about the host. get-key sends the name, position, company, email and purpose you give it, only after --accept-terms.

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. lattice-scan get-key also stores a key there as the file key (mode 0600, never printed). enrich and project write nothing.

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-range is 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_lookup is 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.3.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for lattice-scan 0.3.2
File Interpreter ABI Platform Actions
lattice_scan-0.3.2-py3-none-any.whl Python 3 none any Details for lattice_scan-0.3.2-py3-none-any.whl

Release files / lattice_scan-0.3.2-py3-none-any.whl

Download URL lattice_scan-0.3.2-py3-none-any.whl
Size 55.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d5b1ec6da595761082c2e03b0a36ebc2d447f3b5e31bc778f2ca5c98fedd1943
BLAKE2b-256 checksum
How to use checksums
a59c40de65d107b296d1f3ec4d83f4d0434238fe70307e47ead9fae14b6bec46
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 log

Security for lattice-scan 0.3.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.3.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 log

A 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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page