VDB
Sign up
Continuously updated

Changelog

Everything we've added or improved on VDB, recorded right after the deploy that ships it.

August 2026

feature

VEX — which of your advisories can actually be reached

A scan on a real project returns hundreds of advisories, nearly all of them about code the application never puts attacker-controlled data into, and a queue nobody can work through is a queue everybody ends up ignoring. `vdb harden ./src --manifest uv.lock --vex` now answers which ones can be reached, and emits it as an OpenVEX v0.2.0 document so any tool that reads the format can subtract the same set. Each run also gets a URL, because the result only changes anyone's behaviour when a team looks at the same list. Point it at the source tree rather than a single file: a not_affected claim is only as wide as the code behind it, and deriving "nothing reaches urllib3" from one file while forty other modules import it would be wrong in a format built to be trusted automatically — below two analyzed files every such claim is withheld. Two things it deliberately never says. It never emits `affected`, because reaching a package is not reaching the specific function an advisory names and stating otherwise would be a guess wearing the clothes of a finding. And it never uses the `vulnerable_code_not_in_execute_path` justification, because the analysis follows tainted data: a package the app calls with only its own constants is still executed. The justification it does use, `vulnerable_code_cannot_be_controlled_by_adversary`, says exactly what was determined. Reachability comes from static per-package summaries, so a relation the analyzer missed produces a false negative; every reachability product has that property and this one states it on the document. Evidence for triage order, not proof of non-exploitability. Also available as `vdb_vex` over MCP in local mode.

fix

uv.lock and poetry.lock were being scanned wrong, not rejected

Neither format had a parser. That would have been fine if it produced an error, but the scanner falls back to requirements.txt parsing, which does not fail on TOML — it matches a couple of stray `key = "value"` lines and returns a confident, tiny component list. A real uv.lock with six packages came back as two components and zero findings, and a user reading that concludes the project is clean. Both formats are parsed properly now, including uv's virtual workspace root (which is the project being scanned, not a dependency), and TOML lockfiles are sniffed before the requirements fallback so an unhelpful filename cannot route them into the wrong parser. Silent partial coverage on a security scan is worse than an error, because an error gets investigated.

feature

MCP is now the way to connect — one command, and two new tools

Connecting used to mean pasting a prompt, and MCP was listed at the bottom of /connect under "other ways". That was backwards: a pasted prompt has to be repeated every session and competes for context, while `claude mcp add --transport http vdb https://vdb.ai.kr/mcp` is one command that stays. MCP now leads on both the homepage and /connect, and the prompt remains as a fallback for tools that cannot speak it. Two tools were missing and are now there. `vdb_scan_lockfile` is the merge gate — checking the packages someone chose misses the transitive ones nobody did, which is usually where the risk is. `vdb_harden` decides whether attacker-controlled data can reach a dangerous operation through your dependencies, with no CVE required. `vdb_harden` is offered only when the server runs on your own machine (`uvx vdb-mcp`), and that is deliberate rather than a limitation: it abstracts your file before anything is sent, and that has to happen on the machine that owns the source. On the hosted endpoint the file paths would be ours, and the only way to make it work there would be to ship your source to us. Two existing tools were also quietly wrong — `vdb_check_package` and `vdb_check_mcp_server` called endpoints that return raw records and no verdict, so they disagreed with the bulk tool about what a check even means. Both now go through the same gate. Failed calls through MCP answer `agent_action: REFUSE` as structured data rather than an error string, so failing closed does not depend on a model reading prose carefully. /connect itself went from 983 lines and a 20,778-character prompt to 336 lines and about 1,000.

fix

uvx vdb-mcp was completely broken — fixed in 0.2.0

The published package declared `mcp>=1.0.0` with no upper bound. The SDK's 2.0 release removed the decorator-based low-level API the server is written against, so a fresh install resolved mcp 2.x and died at import with an AttributeError before serving a single request. There is no useful symptom for this: the client simply reports a server that will not start. Anyone who tried the `uvx vdb-mcp` path — one of the two connection routes the homepage advertises — got nothing. The dependency is now pinned below 2 in both the package and the container image, and a test fails if that bound is ever removed without a matching migration to the 2.x API. If you had this configured and it was silently doing nothing, upgrading to 0.2.0 fixes it.

fix

npm slopsquatting detection was never firing — fixed

The npm registry probe asks for the abbreviated packument, which deliberately omits the `time` block. Our age heuristic read `time.created`, so every npm package resolved to "publish date unknown" and fell through to the safest verdict. In practice that meant the npm half of the freshly-registered-squat check had never fired once: a name registered yesterday scored the same as lodash. Two cheap signals replace it — the release count from the same response, and last-week downloads from an 82-byte endpoint. Two consequences. A name that is registered but has never published a release is now high risk: it resolves, so a naive existence check passes, and whoever holds it can publish anything under it later, under a name the model already trusts. And "we could not establish age or usage" no longer reads as "low" — unknown is not the same as safe, and treating it as safe is what hid this for so long.

api

Responses now carry the verdict — agent_action, and a 60% shorter prompt

Every result from /v1/ai/check-packages and /v1/sbom/scan now carries `agent_action` (REFUSE, CONFIRM, or PROCEED) and a short `because` citing the evidence. The gate rules that produce it — not_found refuses, KEV or EPSS at or above 0.5 refuses regardless of CVSS, an unverified MCP publisher asking for exec or filesystem scope refuses even with a clean record — used to live in the prompt you pasted, which meant the model had to hold them in context for an entire session. It didn't: context decay quietly switching the gate off was the most common way this stopped working. Deciding server-side means the instruction arrives with the answer, on every call. The published prompt drops from 2,617 to about 1,040 characters (down 82% from the original 5,420), and 14 of 14 live agent scenarios pass. Error responses decide too: a 429, a timeout, or an exhausted trial answers `agent_action: REFUSE`, so failing closed no longer depends on the model remembering to. One policy change came out of this: an MCP server VDB has never seen is now REFUSE rather than CONFIRM. An unknown library is a contained bet; installing an unknown MCP server hands an unvetted publisher the session's tools, filesystem, and secrets. Same verdict, very different exposure.

ui/ux

Homepage rebuilt around three moments, and a new /harden page

The homepage had grown into a feature inventory: a hero, a search box, a prompt, a changelog, a VS Code card, an SBOM Watch card, four coverage pillars (two of them labelled "coming soon"), two more stat cards, six vulnerability cards, a three-column competitor comparison, and a promo popup. Every block was individually defensible and the sum said nothing. It now tells one story — the three moments a bad dependency gets into a codebase, and the fact that VDB is at all three: when your agent picks it (the name may not exist), before it merges (the risky package is usually one nobody named), and after it ships (a new advisory, or a risky dataflow path that has no CVE at all). Each moment carries a concrete example rather than a claim, because "we detect supply-chain risk" means nothing and `pkg:npm/react-hook-forms → not_found` means everything. The primary action is the one-paste policy prompt; search moved below the value story, and the "coming soon" pillars were cut outright — advertising the absence of a feature is worse than staying quiet about it. Call-site hardening also gets a real destination at /harden, covering what it returns, exactly what does and does not leave your machine, how a path is decided, how re-verification proves you closed it, and what it structurally cannot cover.

change

VDB is now English-only — /en/* redirects to /

The site used to render every page twice, Korean at /foo and English at /en/foo. That is over: there is one document per URL now, and every /en/* address 301s to its unprefixed form, so existing links and bookmarks keep working and search engines move the ranking signal to the surviving URL. The change goes all the way down rather than stopping at the navigation bar — emails (verification, API key, SBOM Watch alerts), API error bodies, and the OpenAPI documentation are English throughout. The `message_ko` field is gone from 401 and 429 responses; clients read `message`, which is unchanged. Two upshots worth knowing: a page no longer needs a hand-written /en mirror file, which was a bug we shipped three separate times, and the duplicate-canonical ambiguity that Search Console kept flagging no longer has anything to be ambiguous about.

feature

Call-site hardening — find risky dataflow before a CVE exists

VDB can now answer a question no advisory database can: given this call site in your code, can attacker-controlled data reach a dangerous operation deep in your transitive dependencies — whether or not a CVE has ever been published for it? Run `vdb harden app.py --manifest uv.lock`. Your source never leaves your machine: the file is abstracted locally into a call-graph IR (identifiers renamed, literals reduced to shapes, bodies dropped) and only that IR plus your lockfile is uploaded — `--emit-ir` shows you exactly what would be sent, with no network call. Paths are decided by composing precomputed per-(package, version) taint summaries over the version-resolved dependency graph, across six sink classes: path operations, command execution, network requests, deserialization, regex evaluation, and global-state mutation. Summaries are built once per version from the real PyPI artifact by AST parsing only — package code is never imported or executed. The fix you get back is applied at your call site; VDB never asks you to patch, fork, or pin the dependency, because you can always fix your own boundary and usually cannot fix someone else's package. After you apply it, `--verify` re-analyzes and issues signed evidence bound to the dependency graph hash — and partial defense does not count as closed: apply two of the three defenses a network sink requires and you get `missing=block-internal-ranges`, not a false attestation. Every answer carries its residual risk: install-time malware, background exfiltration, and unanalyzed dynamic loads are never marked defended, and hops that had to be assumed conservatively lower the stated confidence instead of quietly disappearing. When a new version of a dependency is summarized, affected analyses are recomposed and you are emailed only about paths that were not there before.

ui/ux

Connect prompt ~53% shorter — less paste friction, same gating behavior

Feedback said the one-line connect prompt was too long to paste comfortably. Rewrote it as terse rules — dropping the per-rule explanations, justifications, and duplicate examples — cutting EN 5,420→2,557 and KO 3,691→1,728 chars (~-53%). The policy skeleton is intact: MUST-strength language, the check-packages/sbom-scan/request-key routes, purl examples (%40/huggingface/data/maven/mcp), per-risk actions (not_found/high/medium/unknown/low), KEV+EPSS auto-refuse, MCP scope orthogonality, fail-closed, lifetime trial, and the no-inventing-keys guard. Verified no behavioral regression: 15 structural tests + 28/28 on the live prompt-eval battery.

fix

check-packages — safe_upgrade suppressed when the pinned version already meets it

/v1/ai/check-packages returned safe_upgrade (the package-wide highest fix) without comparing it to the queried version, so clients told users already on a safe pin to "patch to X" where X was their current version — a real python-multipart 0.0.31 report. safe_upgrade is now null when the pinned version meets the target (kept conservatively when versions can't be compared). VS Code extension 0.4.0 adds the same guard client-side plus explicit "Safe version: X (you have Y)" wording.

July 2026

feature

VS Code extension 0.4.0 — inline diagnostics for pyproject.toml & pom.xml

The extension's live diagnostics were skewed toward package.json/requirements.txt; this release widens them. pyproject.toml: PEP 621 ([project] dependencies / optional-dependencies, single-line and multi-line arrays) and Poetry ([tool.poetry.dependencies] plus group tables), with diagnostics on the exact dep line. pom.xml: each is checked as pkg:maven/group/artifact@version, ${property} versions resolve from , BOM-managed (version-less) deps are name-checked, and test-scope deps are skipped. The "pin to latest" quick-fix now supports Maven Central too.

feature

Remote MCP endpoint — https://vdb.ai.kr/mcp (no install)

Opened a remote MCP endpoint so you can connect to VDB with just a URL — no install. Any streamable-HTTP MCP client (Claude Desktop, Cursor, …) takes { "vdb": { "url": "https://vdb.ai.kr/mcp" } } and gets all 6 tools (package/bulk check, lookup, search, MCP-server trust, slop list). Anonymous sessions share the per-IP free trial; users pass their own API key via session config for per-account metering. The local uvx vdb-mcp path still works too.

feature

vdb-mcp 0.1.1 — published to PyPI + listed in the official MCP registry (kr.ai.vdb/vdb)

vdb-mcp is now on PyPI — any MCP client connects to VDB with a single "uvx vdb-mcp" line. Also listed in the official MCP registry (registry.modelcontextprotocol.io) under the kr.ai.vdb/vdb namespace, verified via vdb.ai.kr DNS (ed25519) — directories that crawl the registry (PulseMCP, Glama, …) pick it up automatically. License: Elastic License 2.0 — free to use, including commercially; only reselling it as a hosted/managed service is prohibited.

seo

Added llms.txt + a citable “Quick fix” block on advisory pages

AI crawlers already index our advisory pages at hundreds of IPs per day. To make answer engines (ChatGPT/Claude/Perplexity) cite VDB precisely, we added /llms.txt (site guide + agent-facing API summary + citation guidance) and a “Quick fix” block at the top of every advisory page carrying the advisory id, package, and exact upgrade command in one liftable sentence.

feature

vdb-mcp installable via uvx + SBOM-scan GitHub Action

Repackaged the MCP server as an installable vdb-mcp package — one line in your client config ("uvx vdb-mcp"), zero configuration, hosted vdb.ai.kr by default (6 tools: package/bulk check, lookup, search, MCP-server trust, slop list). Also added a CI GitHub Action: one step (uses: 0pstech/vdb-scan-action@v1) scans your lockfile, gates the build on a fail-on severity threshold, and writes a job-summary table with advisory links and upgrade commands. A free API key is recommended in CI — anonymous calls share the runner IP pool's small lifetime trial.

ui/ux

Trial-exhausted responses are now AI-agent-actionable

Reworked the trial-exhausted bodies on /v1/ai/check-packages (429) and /v1/sbom/scan (401). They now always carry error: free_trial_exhausted plus signup_url, request_key_url, docs_url, and a bilingual message. The key addition is request_key_url — the passwordless path where a browserless AI agent just asks its human for an email, POSTs it, and a free key arrives by mail. The old body only said "sign up", which agents answered by retrying the same call hundreds of times and then giving up.

fix

SBOM Watch email language now defaults to your signup language

The language you sign up in (ko/en) is now stored on the account (users.lang). When an SBOM Watch registration carries no language signal, alerts follow it. Priority: explicit lang field at registration → the request's X-VDB-Lang/Accept-Language → signup language → ko. Previously, watches registered via the JSON API always received Korean mail — accounts that signed up in English now get English alerts.

feature

SBOM Watch — email alerts when a dependency in your registered SBOM gets an advisory (members)

/v1/sbom/scan was a point-in-time snapshot. Members can now register an SBOM (or lockfile) at /sbom-watch; a worker re-checks the stored components against the vulnerability DB every hour and emails you only when a NEW advisory matches. Each mail carries the facts (package, version, severity, KEV status) and the fix (fixed versions, per-ecosystem upgrade command, package-wide safe upgrade target). Every (SBOM, vulnerability, package) triple is notified exactly once — no repeat spam. Limits: 5 watches per account, 20,000 components per file. API: POST/GET/DELETE /v1/sbom/watches.

fix

Removed scan false positives caused by version-resolver failures

When scanning range specs rather than pinned versions (e.g. >=0.115, ^0.21.0 in requirements.txt / package.json), a failed registry lookup (offline, blocked, or rate-limited) left the spec unresolved, so the finding was kept to err safe — over-reporting (e.g. FastAPI's CSRF, fixed in 0.65.2, still flagged high at >=0.115). A finding is now excluded only when it is provably true, without any network call, that the spec's entire satisfiable version set lies outside every affected interval. The check is one-directional: it can remove a false positive but never hides a real vulnerability.

fix

False-positive fix for advisories with bad upstream data (uvicorn CVE-2020-7694)

GHSA-33c7-2mpw-hg34 (uvicorn log injection) is recorded upstream as "all versions affected, no patch" even though maintainers fixed it in 0.11.7 — flagging every uvicorn release as high. A serving-time correction layer now suppresses provably-fixed versions and attaches an override_note explaining any divergence from raw OSV.

June 2026

feature

VS Code & Cursor extension launched — inline slopsquatting / CVE / registry checks as you edit

Open or save package.json / requirements.txt / Cargo.toml / go.mod / SBOM and the extension extracts every dependency, scores it via POST /v1/ai/check-packages, and draws the verdict inline. Slopsquatted (non-existent) names flag as errors; known CVEs get a one-click upgrade to a safe version. Works with no account. Published to Marketplace and Open VSX.

security

SBOM upload — five-layer hardening for malformed / huge / deeply-nested files

Concentric defenses on the /v1/sbom/scan upload path. (1) Content-Length pre-check rejects oversized uploads before the read. (2) The read itself is capped to SBOM_MAX_BYTES+1 so a lying body can't sneak past. (3) Filename is sanitised — basename only, ../, NUL, and control bytes stripped, 255-char cap, so nothing weird flows into the format detector or audit log. (4) Parser failures broaden to catch RecursionError (deeply-nested adversarial JSON), MemoryError (billion-laughs-style blowups), and stray decode errors — all map to 400/413 instead of 500. (5) Post-parse filter drops purls / versions over 1024 chars or carrying control bytes, then caps total component count per call (5k anonymous, 20k authenticated). A new components_truncated field in the response tells the caller how many rows were dropped so they can decide whether to split the SBOM. 14 unit tests cover both helpers.

ui/ux

SBOM scan — 3 free anonymous scans per IP + helpful 401 with signup info

/v1/sbom/scan previously required an API key on the first call, returning a terse "missing bearer token" 401 with no hint at the fix. Anonymous callers now get 3 lifetime scans per IP (check-packages is 50), and the response carries a trial { used, limit, remaining } block. After exhaustion the 401 body becomes { error: "free_trial_exhausted", message, signup_url, docs_url } so callers know exactly what to do. The cap is tighter than check-packages because SBOM parse + bulk vuln JOIN is materially more expensive per call. /api docs updated with the new behaviour + sample response.

docs

About page — CISA KEV / FIRST EPSS added to the data-sources table

The "data sources" table at the bottom of /about now lists CISA Known Exploited Vulnerabilities and FIRST EPSS, with their daily cadence and upstream links. Visitors can audit where the kev / epss fields in our responses come from. The sample-response JSON on the same page also now shows both fields.

data

CISA KEV + FIRST EPSS signals on check-packages / SBOM responses

The new daily kev-epss collector pulls CISA Known Exploited Vulnerabilities (~1.1k entries) and FIRST EPSS scores (~250k CVEs) into ai_signals. /v1/ai/check-packages and /v1/ai/sbom-scan now return kev (boolean) and epss (0..1) per advisory. KEV=true or EPSS≥0.5 escalates the verdict to high regardless of base CVSS — Standard-tier consumers automatically catch "low CVSS but already being exploited" advisories. Sort order also changes to KEV → EPSS → CVSS so truncated clients (read only vulns[0]) get the right top hit.

data

Data milestone — crossed 500k AI datasets

Total collected AI datasets crossed 500,000 for the first time. Auto-emitted by the scheduler.

data

Data milestone — crossed 500k AI models

Total collected AI models crossed 500,000 for the first time. Auto-emitted by the scheduler.

data

Data milestone — crossed 250k vulnerabilities

Total collected vulnerabilities crossed 250,000 for the first time. Auto-emitted by the scheduler.

ui/ux

Footer — partnership & service contact line added

A third slot alongside copyright and bug-report. mailto: link in font-mono; wraps cleanly on narrow viewports.

i18n

Comprehensive ko/en drift fix across account, auth, connect, api, admin pages

About twenty pages where /en/* mirrors leaked Korean strings are now fully bilingual. 90+ new i18n keys; ko/en check is now a standing rule for every page edit.

fix

Package matching — auto-normalise crates.io / RubyGems / Go user input to purl-spec canonical types

User input like pkg:crates.io/tokio now resolves against pkg:cargo/tokio internally. Closes a silent false-negative gap.

May 2026

feature

Reachability prompt split — members-only Standard + Reachability (§11)

Even with a CVE present, downgrade to advisory when the user's code path doesn't reach the affected function.

feature

MCP servers — scope drift detection + pkg:mcp/ identifier support

Silent capability elevation in minor releases is surfaced via the new mcp.scope_drift response field.

feature

AI dataset gating — pkg:data// covers license + PII signals in the unified gate

Full Hugging Face dataset catalogue mirrored. License + PII alongside model weights_format in one gate.

feature

SBOM merge gate — coverage_ratio in response + self-SBOM at /sbom.cdx.json

Coverage ratio surfaced on lockfile scan responses; VDB's own SBOM published for external verification.