Scan database versions for known vulnerabilities
Match a self-managed database version against pinned local server advisories.
Edge documents the current development build and may describe unreleased behavior. To use those commands, build Ptah from master. For an installed release, select its version in the header.
ptah db vulnerabilities reads the product and version of a live database and
matches them against a pinned local server advisory snapshot. You can run
it in CI without a Ptah account or an advisory service. The scan reads the
database and the local snapshot; it does not upload inventory.
This check answers whether a known advisory’s version range matches the
observed server. ptah schema security checks roles,
grants, ownership, and policies instead. A clear result from either command is
limited to what that command assessed.
Run the scan
Section titled “Run the scan”You need an installed ptah binary and a database URL. The default
--deployment self-managed means your team controls server patching. For
PostgreSQL, the connected role also needs to read pg_catalog.pg_extension.
The command reads the version banner; PostgreSQL also reads the numeric server
version and extensions in the connected database. It does not change the database.
ptah db vulnerabilities --db-url "$DATABASE_URL"The table report names each affected or uncertain advisory, its component,
installed server version, advisory URL, CVSS score when supplied, and known
fix. It also lists gaps and summarizes fixed and unaffected matches. The
--format json report keeps every assessment, including fixed and unaffected
ones, plus the inventory, source identity, and incomplete coverage.
ptah db vulnerabilities --db-url "$DATABASE_URL" --format json > vulnerabilities.jsonThe JSON source block includes the upstream URL, a SHA-256 of the source page,
a SHA-256 of the normalized snapshot, retrieval time, age in days, and
current, stale, or unavailable. The assessments array uses affected, fixed,
unaffected, or uncertain. The separate incomplete array names coverage
gaps. applicability distinguishes assessed, unassessed, and
not_applicable. Failed snapshot input or database inventory produces a
scan_error in JSON and exits with code 2.
If a provider operates the server, select --deployment managed. The report
records not_applicable, makes no advisory assessment, and does not fail the
default CI policy. This override works for every engine, including YDB. Use
self-managed when your team controls the server version, even if it runs in
a cloud account. Ptah does not infer patch responsibility from the endpoint.
ptah db vulnerabilities --db-url "$DATABASE_URL" --deployment managed --format jsonChoose the CI policy
Section titled “Choose the CI policy”The default --fail-on both exits 1 for an unsuppressed affected advisory or
incomplete coverage. Select findings, incomplete, or none when your gate
needs a different policy. Input and connection failures exit 2 under every
policy.
ptah db vulnerabilities --db-url "$DATABASE_URL" --fail-on both --format jsonTo accept one affected advisory temporarily, write a local JSON suppression file. Its ID, product, component, and reason are required. An expiry is optional. An expired or invalid suppression fails the scan. Suppressed assessments remain in the JSON report with the reason.
[ { "id": "CVE-2026-18024", "product": "postgres", "component": "core", "reason": "Vendor patch verified in change ticket SEC-42", "expires": "2026-12-31" }]ptah db vulnerabilities --db-url "$DATABASE_URL" --suppressions suppressions.jsonCheck coverage before trusting the result
Section titled “Check coverage before trusting the result”The engine declares whether Ptah can match its server version. Ptah bundles the built-in engine snapshots. The YDB provider bundles its own snapshot:
| Engine | Reviewed source | Coverage |
|---|---|---|
| PostgreSQL | PostgreSQL security page | Core and selected bundled modules on listed upstream release lines. |
| YDB provider | YDB security changelog | The provider owns server entries; the Go SDK entry is outside server scope. |
| MariaDB | Community Server CVE list | Three selected server CVEs; the report remains incomplete. |
| ClickHouse | Security changelog | One selected server CVE on four release lines; the report remains incomplete. |
A provider that declares scanning but supplies no reviewed snapshot returns
unassessed unless --advisories selects a local source.
The PostgreSQL snapshot names its supported lines in release_lines. It
matches core advisories and entries naming pgcrypto, pg_trgm, intarray,
or refint. Their fixes ship with the server minor release: extversion
records the installed module, not its patch level. The extension catalog
covers only the connected database. Client-only advisories are excluded.
DuckDB and Spanner declare a server scan not_applicable: DuckDB is embedded,
while Spanner is a managed service. SQLite returns unassessed because a
libsql:// address can reach a remote server. Other built-in engines without
a reviewed matcher also return unassessed. An
unsupported release line, vendor build, or unrecognized
version remains uncertain because a distribution may backport a fix.
Version matching also cannot establish whether a vulnerable code path is
reachable. Investigate the linked advisory and your vendor’s patch record
before deciding how to remediate.
The snapshot becomes stale 30 days after retrieval. A stale snapshot remains visible in the report and counts as incomplete coverage. Refresh it before using an empty finding list as a current assessment.
Update the local snapshot
Section titled “Update the local snapshot”The daily Server advisory snapshots workflow in Ptah checks PostgreSQL,
MariaDB, and ClickHouse. The YDB provider runs its own daily source check and
bundles the reviewed result in its executable. Both workflows open a draft
pull request when a snapshot needs a refresh. PostgreSQL rows are parsed and
validated before the snapshot is written. An unchanged versioned source can
refresh its retrieval time after 14 days.
The YDB provider records both the raw documentation hash and the advisory
body hash. A changed documentation revision outside the advisory body creates
no review report. A changed advisory body produces a JSON report in the
provider’s internal/vulnerability/review/ directory. The report lists
observed CVE IDs and source hashes; it does not establish affected ranges.
Verify server CVEs, affected starts, fixed versions, and release lines before updating a snapshot. Remove its review report before merging. Partial coverage remains partial until the reviewed snapshot covers more advisories.
To run the daily source check locally, use:
go run ./internal/cmd/securitysnapshotsFrom a source checkout, run the updater and review the JSON diff:
go run ./internal/cmd/pgsecuritysnapshotgit diff -- internal/vulnerability/postgresql.jsonThe updater fetches the PostgreSQL security page
and records its source digest and retrieval time. Use -input saved.html to
parse a saved copy without network access. It refuses missing headings, table
columns, affected/fixed mismatches, unknown components, and empty coverage.
The scan itself never fetches advisories. You can select a reviewed local JSON
file with --advisories path/to/snapshot.json.
Review newly named components, release-line changes, and the advisory source before committing an update. The snapshot derives from PostgreSQL’s published security information under the PostgreSQL License. The source link, digest, and license notice travel with the snapshot in the repository.
An installed Ptah binary carries the built-in snapshots. A provider executable
carries its own snapshot. Update the relevant binary or use --advisories to
select another reviewed local file. For a versioned engine, the snapshot uses
schema version 2. Review each affected start, fixed version, release line,
and CVE source before updating its JSON in the owning repository. Pin the
upstream page’s SHA-256 and retrieval time.
Set coverage to partial with a reason when the snapshot omits advisories;
the scanner then reports incomplete coverage. A source URL or hash alone does
not prove that its affected ranges were reviewed.