Skip to content
solongatePublic

About

Map newly disclosed vulnerabilities to the product releases you’ve shipped.

Resources

Code of conduct

Contributing

Security policy

Stars

20 stars

Watchers

0 watching

Forks

SolonGate PSIRTMap

PSIRTMap

Map newly disclosed vulnerabilities to the product versions you have actually shipped.

Local-first product-security inventory and impact analysis for device, firmware, and embedded-software manufacturers.

Release CI Go License

Security Badges
OpenSSF Best Practices Passing OpenSSF Best Practices Silver OpenSSF Best Practices Gold
OpenSSF Baseline 1 OpenSSF Baseline 2 OpenSSF Baseline 3
OpenSSF Scorecard openssf Allstar

SLSA Build Level 1 SLSA Build Level 2 SLSA Build Level 3

REUSE status

Security Rating Reliability Rating Maintainability Rating
Quality Gate Coverage

PSIRTMap — vulnerability impact tracking for shipped products

Important

PSIRTMap is pre-1.0. Normal release scans use local OSV and CISA KEV snapshots and do not require internet access after psirtmap sync. Versioned feed bundles can transfer those snapshots into machines that never connect to the internet.

Why PSIRTMap?

A package scanner can tell you that openssl@3.0.8 matches a vulnerability. PSIRTMap is built to answer the product-security question that follows:

Which versions of the products we shipped may contain that component?

Package scanner PSIRTMap
Primary object Image, filesystem, or package Product release
Core question Is this package version vulnerable? Which shipped releases may be affected?
Result Package-level match Release-level finding that needs human review

PSIRTMap deliberately reports matches as potential impact. A version match is useful evidence; it is not proof that a shipped product is exploitable.

What works today

The current release, v0.0.11, includes:

  • A responsive, keyboard-driven terminal dashboard.
  • A guided first-run flow that creates a product, release, and component inventory from one CycloneDX file.
  • Local SQLite inventory for products, releases, and components.
  • Atomic CycloneDX JSON import with package URL normalization and provenance.
  • Guided product, release, and component creation.
  • Atomic OSV synchronization for the package versions in the local inventory.
  • Local CISA Known Exploited Vulnerabilities (KEV) synchronization and alias-aware finding enrichment.
  • Local release scanning with source and snapshot freshness metadata.
  • Durable findings with new, existing, reopened, and no-longer-matched states.
  • Normalized CVSS 2.0, 3.0, 3.1, and 4.0 severity with explicit UNKNOWN handling when source data is missing or invalid.
  • Fixed-version boundaries, disclosure timestamps, advisory references, and original CVSS context on durable findings.
  • Deterministic finding priority: CISA KEV first, then severity, disclosure recency, and stable identity.
  • Release, assessment-status, severity, and KEV filtering plus a detailed findings show view.
  • Append-only human assessments with investigating, affected, not-affected, and fixed decisions.
  • Reviewer, reason, evidence, timestamp, and complete decision history.
  • Versioned, checksummed offline feed export and atomic import.
  • Dashboard actions for online updates and offline feed import/export.
  • Persistent bundle provenance, source freshness, and import timestamps.
  • Optional Ed25519 feed signing with explicit trusted-public-key verification.
  • Dashboard actions for signing-key generation, verified offline feed import, and signed feed export.
  • Persistent signature status and signer identity for imported bundles.
  • An explicit --live scan mode for direct OSV checks.
  • Human-readable and deterministic JSON output.
  • Single-binary builds for macOS, Linux, and Windows.
  • Checksum-verifying installers and a local readiness doctor.
  • Private pre-migration database backups, compatibility fixtures, parser fuzz seeds, JSON output contracts, and reachability-aware dependency checks.
  • No account, database server, or PSIRTMap cloud service.

See the roadmap for the ordered path to VEX and later PSIRT workflow capabilities.

Install

Install the latest release on macOS or Linux:

curl -fsSL https://raw.githubusercontent.com/solongate/psirtmap/main/scripts/install.sh | sh

The installer selects the current platform archive, verifies its published SHA-256 checksum, and places psirtmap in a normal executable directory. You can review the installer before running it.

On Windows PowerShell:

irm https://raw.githubusercontent.com/solongate/psirtmap/main/scripts/install.ps1 | iex

The Windows installer verifies the release checksum, installs the executable under the current user's local programs directory, and adds that directory to the user PATH.

Other installation methods

Download the archive for your platform from the latest release and verify it with the published checksums.txt file. Each release archive also carries a signed build provenance attestation you can verify with the GitHub CLI:

gh attestation verify psirtmap_<version>_<os>_<arch>.tar.gz --owner solongate

Each release also publishes a CycloneDX SBOM for PSIRTMap's own build (psirtmap_<version>_sbom.cdx.json) covering its Go module dependencies and their licenses.

Go users can install the latest tagged release directly:

go install github.com/solongate/psirtmap@latest

Ensure Go's binary directory is on PATH after go install.

PSIRTMap requires no SQLite installation or background service.

Uninstall

If PSIRTMap was installed with install.sh, on macOS or Linux:

curl -fsSL https://raw.githubusercontent.com/solongate/psirtmap/main/scripts/install.sh | sh -s uninstall

On Windows PowerShell, pass -Uninstall to the installer script you downloaded:

./install.ps1 -Uninstall

Either command removes the installed binary and, on Windows, the PATH entry added at install time. A local PSIRTMap database is never touched by install or uninstall; remove it yourself if you no longer need it (see Local data).

Start in 60 seconds

Open the dashboard:

$ psirtmap

That is the complete launch command. The first run creates ~/.psirtmap/psirtmap.db and opens a guided setup. Press Enter and provide the product name, release version, and CycloneDX JSON file. The dashboard then guides you through updating vulnerability data, scanning the release, and reviewing findings. Shortcuts are optional.

Key Action
↑ / ↓ or j / k Move through sections or rows
← / → or h / l Move between navigation and content
1–7 Open a section directly
Enter Run the highlighted or recommended action
n Create an item in the current section
i Import CycloneDX JSON from Releases
Ctrl+O Browse for a file while a file field is active
a or Enter Assess the selected finding from Findings
u Update local OSV and CISA KEV data; this step uses the internet
s or Enter Scan the selected release
r Refresh local inventory
? Show keyboard help
q Quit

Launch explicitly or use an isolated database:

psirtmap dashboard
psirtmap --database ./demo.db dashboard
Prefer scriptable commands?

The dashboard and CLI operate on the same local database.

psirtmap product add AG-200 --description "Industrial gateway"
psirtmap release import AG-200@2.2 ./firmware-2.2.cdx.json

psirtmap sync
psirtmap scan AG-200@2.2
psirtmap findings AG-200@2.2
psirtmap findings AG-200@2.2 --severity critical
psirtmap findings AG-200@2.2 --kev
psirtmap findings show AG-200@2.2 CVE-2026-12345
psirtmap assess AG-200@2.2 CVE-2026-12345 \
  --status not-affected \
  --reviewer emirhan \
  --reason "Vulnerable functionality is disabled in this firmware build"
psirtmap assess history AG-200@2.2 CVE-2026-12345

The import accepts CycloneDX JSON 1.2–1.7, walks nested components, derives OSV identities from package URLs, records the document hash, and safely merges repeated imports. Components without a supported package URL are reported as skipped. Manual component entry remains available for demos and corrections. An example device SBOM is included.

Query one package without adding it to the inventory:

psirtmap check jinja2 2.4.1 --ecosystem PyPI

Use a direct OSV query without changing the saved snapshot:

psirtmap scan AG-200@2.2 --live

Use JSON output in scripts:

psirtmap product list --json
psirtmap release import AG-200@2.2 ./firmware-2.2.cdx.json --json
psirtmap sync --json
psirtmap feed export ./psirtmap-feed.bundle --json
psirtmap feed import ./psirtmap-feed.bundle --json
psirtmap scan AG-200@2.2 --json
psirtmap findings --all --json
psirtmap findings show AG-200@2.2 CVE-2026-12345 --json
psirtmap assess history AG-200@2.2 CVE-2026-12345 --json

Authenticated-feed commands also support JSON:

psirtmap feed keygen --private-key ./feed-key.pem --public-key ./feed-key.pub.pem --json
psirtmap feed export ./psirtmap-feed.bundle --signing-key ./feed-key.pem --json
psirtmap feed import ./psirtmap-feed.bundle --trusted-key ./feed-key.pub.pem --json

Run psirtmap help or psirtmap <command> --help for complete usage. Run psirtmap doctor to see the database path, inventory counts, feed freshness, and the next action required before scanning.

How it works

CycloneDX SBOM -> Product release -> Component inventory
                                         |
OSV API --------- psirtmap sync -------> local OSV snapshot
CISA KEV catalog -/             \------> local KEV snapshot
                                         |
                              offline release scan -> durable finding -> human assessment

For example:

AG-200
└── 2.2
    └── OpenSSL 3.0.8
        └── CVE / OSV match
            └── NOT AFFECTED
                └── reviewer + reason + evidence + timestamp

During sync, PSIRTMap sends each distinct component ecosystem, package name, and version to OSV. It stores the matching advisory metadata, aliases, CVSS vectors, affected ranges, fixed boundaries, references, retrieval time, and exact package-version match in SQLite. Supported CVSS 2.0, 3.0, 3.1, and 4.0 vectors are normalized into a severity label without changing the source vector. It also downloads CISA's complete public KEV catalog and stores the catalog version, freshness, CVE metadata, required action, due date, and ransomware-use signal. Product names, release names, descriptions, and complete SBOM files are not sent to either source.

Normal scan commands read those saved snapshots and make no remote request. A finding is marked as known exploited when its OSV identifier or one of its aliases matches a KEV CVE. KEV is a prioritization signal; it does not prove that a particular product configuration is exploitable. A new or changed component needs another successful sync; a failed source update preserves the last known-good local data. check, sync, feed pull, and scan --live are the operations that require internet access.

Finding lists put KEV matches first, followed by normalized severity and source recency. An OSV fixed boundary means that a source range ends at that version; it is not an automatic recommendation or proof that upgrading alone remediates a particular product.

Air-gapped transfer

Generate a signing key pair once on a controlled, connected staging machine. Keep the private key secret there. Provision the public key to isolated machines through an independently trusted process:

psirtmap feed keygen \
  --private-key ./psirtmap-feed-signing-key.pem \
  --public-key ./psirtmap-feed-signing-key.pub.pem

Create the same component inventory used in the isolated environment, then download, sign, and export its intelligence:

psirtmap --database ./staging.db feed pull
psirtmap --database ./staging.db feed export ./psirtmap-feed.bundle \
  --signing-key ./psirtmap-feed-signing-key.pem

Move the .bundle through the organization's approved transfer process. Do not move the private key. On the isolated machine, require authentication with the provisioned public key:

psirtmap feed import ./psirtmap-feed.bundle \
  --trusted-key ./psirtmap-feed-signing-key.pub.pem
psirtmap scan AG-200@2.2

The bundle contains the inventory-scoped OSV matches and the complete CISA KEV catalog. It does not contain product names, releases, SBOM documents, findings, assessments, or customer data. The connected staging inventory must therefore cover every package version that the isolated installation needs to scan.

Each bundle is a versioned ZIP with a manifest and SHA-256 checksum for every data file. A signed bundle authenticates that exact manifest with Ed25519; the manifest authenticates the payload checksums. Import with --trusted-key rejects unsigned bundles, unknown signers, invalid signatures, and modified content before replacing either local snapshot. OSV and KEV activation plus provenance recording happen in one database transaction.

For backward compatibility, imports without --trusted-key still accept legacy unsigned bundles and label signed bundles as unverified. This mode checks integrity but does not authenticate the creator. Security-sensitive air-gapped workflows should always provision a public key separately and use --trusted-key. Key files and bundles are never overwritten automatically.

Every successful normal scan reconciles its matches with the release's saved finding history. A first match is new, a repeated match is existing, a returning match is reopened, and a finding missing from the latest complete scan becomes no-longer-matched without losing its history. scan --live is diagnostic and never changes durable findings.

An assessment changes the finding's current review state while appending a history entry. Final decisions—affected, not-affected, and fixed—require a reason. investigating may be recorded while evidence is still being collected. Re-scanning refreshes match evidence without erasing or resetting the human decision.

The explicit data model is:

PRODUCT -> RELEASE -> COMPONENT -> VULNERABILITY -> FINDING -> ASSESSMENT

Vulnerability records, KEV metadata, findings, and assessment history all live locally.

Local data

The default database is ~/.psirtmap/psirtmap.db. PSIRTMap creates it with permissions 0600 and applies compatible schema migrations automatically. It contains shipped-product inventory, the active OSV and CISA KEV snapshots, feed-import provenance, scan audit records, durable finding history, and append-only human assessments.

Before an existing database is migrated, PSIRTMap creates a consistent, permission-restricted backup beside it. If that backup cannot be completed, the migration does not start.

Choose another database with either:

psirtmap --database ./demo.db dashboard
PSIRTMAP_DB=./demo.db psirtmap product list

The explicit --database option takes precedence over PSIRTMAP_DB.

Build from source

PSIRTMap requires Go 1.27.2 or newer.

go test ./...
go test -race -count=1 ./...
go vet ./...
go build -o psirtmap .

CI verifies formatting, dependencies, known reachable Go vulnerabilities, static analysis, race-enabled tests, and release builds for macOS, Linux, and Windows.

Dependencies

Direct and indirect Go dependencies are pinned in go.mod and go.sum. Dependabot opens a pull request when a dependency has an update, and CI runs govulncheck on every push and pull request to catch known, reachable vulnerabilities before they reach a release. A new dependency is added only when it is actively maintained, carries an OSI-approved license, and its license text is recorded in THIRD_PARTY_NOTICES.md.

Project information

License

Copyright 2026 SolonGate. Licensed under the Apache License 2.0. See NOTICE and third-party notices.

About

Map newly disclosed vulnerabilities to the product releases you’ve shipped.

Resources

Code of conduct

Contributing

Security policy

Stars

20 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages