npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vibeguard-dev/local

v1.6.0

Published

Static SQL safety analysis for AI agents — catch dangerous queries before they reach your database.

Readme

@vibeguard-dev/local

Static SQL safety analysis for AI agents. Catch the dangerous queries before they reach your database.

npm version License CI


What it does

Your AI agent generates a SQL query. Before you run it, @vibeguard-dev/local checks the query's structure for known footguns: missing WHERE clauses, cartesian explosions, type-coercion bugs, recursive CTEs that don't terminate, irreversible DROP / TRUNCATE, over-fetching projections. 36 senior-DBA-level checks, all static, sub-millisecond, zero network calls.

Known limitations

VibeGuard is a static, schema-blind analyzer. It checks the shape of a query, not its run-time effect. A few boundaries you should know about before you ship it into CI:

  • Literal tautologies in WHERE are caught (since v1.6.0). SQL-034 fires at block / 95 on UPDATE / DELETE whose WHERE reduces to a literal tautology — WHERE 1=1, WHERE true, WHERE id = id, WHERE NOT false, WHERE 'a' = 'a'. The cross-join variants (UPDATE … FROM / DELETE … USING without a join predicate) are caught by SQL-035 and SQL-036 at block / 90.

    What is still NOT caught: semantic tautologies that depend on schema knowledge, such as DELETE FROM users WHERE id IS NOT NULL on a NOT NULL PK column, or DELETE FROM users WHERE id IN (SELECT id FROM users). These require column-nullability or correlated- reference tracking and are out of scope for the local SDK.

  • Schema-aware checks are out of scope. The SDK does not know which columns are NOT NULL, which columns are foreign-key targets, or which tables hold sensitive data. Rules that would require that context (e.g. "this WHERE col IS NOT NULL is a no-op because col is the NOT NULL PK") are deliberately not in local.

  • Run-time effects are not modeled. The analyzer does not execute the query, plan it, or evaluate constants. WHERE 1=1 and WHERE current_timestamp > '1970-01-01' are static-tautological in the same way; the SDK treats both as "has a WHERE clause".

The trade-off is intentional: every check runs offline, sub-millisecond, with zero network calls and zero schema dependencies. If you need schema-aware analysis, that's the cloud product.

Quickstart

npm install @vibeguard-dev/local libpg-query

libpg-query is a peer dependency — install it alongside the SDK. Server-side Node only for the initial release; browser support is out of scope for now.

One-shot CLI

The fastest way to see what the SDK does:

npx @vibeguard-dev/local init

That scaffolds an example SQL file, runs the analyzer on it, prints the catch with severity and fix, and adds an npm run lint:sql script to your package.json you can wire into CI. For ongoing use:

vg-local analyze 'src/**/*.sql'
# Exits 0 if no block-severity catches; 1 if any. CI-friendly.

vg-local analyze 'src/**/*.sql' --fix-dry-run
# Print a unified diff of what --fix would change. Read-only.

vg-local analyze 'src/**/*.sql' --fix
# Apply autofixes in place. SQL-005 / SQL-006 / SQL-001 / SQL-011
# have fixers; other rules surface their catches unchanged.

ESM

import { analyze, init } from "@vibeguard-dev/local";

await init(); // one-time WASM-parser bootstrap

const result = analyze(`UPDATE users SET email = '[email protected]'`);

if (result.catches.length > 0) {
  console.error(result.catches[0]);
  // {
  //   code: 'SQL-003',
  //   title: 'Unbounded UPDATE statement',
  //   severity: 'block',
  //   confidence: 99,
  //   detail: 'UPDATE on `users` has no WHERE clause. Every row in the table will be modified...',
  //   fix:    'Add a WHERE clause that scopes the update to specific rows...',
  //   threatCategories: ['destruction'],
  // }
}

CommonJS

const { analyze, init } = require("@vibeguard-dev/local");

(async () => {
  await init();
  const result = analyze("DELETE FROM users");
  console.log(result.catches[0]?.code); // 'SQL-003'
})();

That's the whole API. After init(), every analyze() call is synchronous and sub-millisecond on typical queries.

What we deliberately do NOT do

This SDK does static analysis only. It checks the shape of your SQL. It does not:

  • Compare your agent's stated intent against what the SQL would actually do
  • Estimate real blast radius from the upstream Postgres planner
  • Provide tamper-evident audit logging
  • Offer human-in-the-loop escalation for grey-zone queries
  • Track per-agent behavioral baselines over time

For those, you want VibeGuard Cloud — the wire-protocol proxy and MCP server this SDK is the static-analysis layer of. Use the SDK locally; use the cloud in production. The two are designed to work together.

The 36 catches

Each catch has a stable code (e.g. SQL-001), a severity, a confidence range, and links to a docs page with examples and references. Catch IDs are forever-stable — once published, an ID always means the same thing (see STABILITY.md).

| Code | Title | Severity | Confidence | Default | Auto-fix | Status | |---|---|---|---|---|---|---| | SQL-001 | Cartesian explosion | block | 90–95 | ON | placeholder | ✅ shipped | | SQL-002 | Self-join footgun | warn | 70–85 | ON | — | ✅ shipped | | SQL-003 | Unbounded UPDATE / DELETE | block | 95–99 | ON | — | ✅ shipped | | SQL-004 | Implicit type coercion in WHERE | warn | 75–85 | ON | — | ✅ shipped | | SQL-005 | NULL comparison footgun | warn | 90–95 | ON | yes | ✅ shipped | | SQL-006 | OFFSET without ORDER BY | warn | 85–95 | ON | placeholder | ✅ shipped | | SQL-007 | NOT IN with nullable subquery | warn | 75–85 | ON | — | ✅ shipped | | SQL-008 | String-concat injection patterns | block | 80–95 | ON | — | ✅ shipped | | SQL-009 | DISTINCT without obvious reduction | info | 60–75 | ON | — | ✅ shipped | | SQL-010 | Correlated subquery in SELECT | warn | 70–85 | ON | — | ✅ shipped | | SQL-011 | Aggregate without GROUP BY | warn | 85–95 | ON | yes | ✅ shipped | | SQL-012 | Recursive CTE without termination | block | 80–95 | ON | — | ✅ shipped | | SQL-013 | DROP / TRUNCATE / DDL destruction | block / warn | 85–99 | ON | — | ✅ shipped (1.1.0) | | SQL-014 | INSERT/UPDATE/DELETE without RETURNING | info | 50 | OFF (opt-in) | — | ✅ shipped (1.1.0) | | SQL-015 | SELECT * over-fetch | info | 60 | ON | — | ✅ shipped (1.1.0) | | SQL-016 | COPY … FROM/TO PROGRAM (server-side RCE) | block | 99 | ON | — | ✅ shipped (1.6.0) | | SQL-017 | CREATE EXTENSION of untrusted procedural language | block | 95 | ON | — | ✅ shipped (1.6.0) | | SQL-018 | ALTER TABLE … DROP COLUMN | warn | 90 | ON | — | ✅ shipped (1.6.0) | | SQL-019 | CREATE TRIGGER (hidden side effects) | info | 75 | ON | — | ✅ shipped (1.6.0) | | SQL-020 | CREATE OR REPLACE FUNCTION (silent overwrite) | info | 70 | ON | — | ✅ shipped (1.6.0) | | SQL-021 | GRANT … TO PUBLIC | warn | 90 | ON | — | ✅ shipped (1.6.0) | | SQL-022 | CREATE/ALTER ROLE … SUPERUSER | block | 95 | ON | — | ✅ shipped (1.6.0) | | SQL-023 | pg_terminate_backend / pg_cancel_backend | warn | 85 | ON | — | ✅ shipped (1.6.0) | | SQL-024 | VACUUM FULL (ACCESS EXCLUSIVE outage) | warn | 80 | ON | — | ✅ shipped (1.6.0) | | SQL-025 | REFRESH MATERIALIZED VIEW (blocking refresh) | warn | 75 | ON | — | ✅ shipped (1.6.0) | | SQL-026 | MERGE with tautological ON | block | 90 | ON | — | ✅ shipped (1.6.0) | | SQL-027 | SET search_path to attacker-controlled schema | warn | 85 | ON | — | ✅ shipped (1.6.0) | | SQL-028 | pg_create_*_replication_slot | warn | 80 | ON | — | ✅ shipped (1.6.0) | | SQL-029 | dblink / CREATE SERVER (outbound network) | warn | 80 | ON | — | ✅ shipped (1.6.0) | | SQL-030 | pg_read_* / lo_export / pg_ls_dir (server FS) | warn | 90 | ON | — | ✅ shipped (1.6.0) | | SQL-031 | INSERT … SELECT … ON CONFLICT DO UPDATE (unbounded upsert) | info | 75 | ON | — | ✅ shipped (1.6.0) | | SQL-032 | EXPLAIN ANALYZE of a destructive statement | info | 80 | ON | — | ✅ shipped (1.6.0) | | SQL-033 | DO $$ … $$ opaque procedural block | info | 70 | ON | — | ✅ shipped (1.6.0) | | SQL-034 | WHERE 1=1 / literal tautology on UPDATE/DELETE | block | 95 | ON | — | ✅ shipped (1.6.0) | | SQL-035 | UPDATE … FROM without join predicate | block | 90 | ON | — | ✅ shipped (1.6.0) | | SQL-036 | DELETE … USING without join predicate | block | 90 | ON | — | ✅ shipped (1.6.0) |

See ROADMAP.md for what's in / out of scope.

Per-rule overrides

Opt in to default-OFF rules, or disable default-ON rules for a single call, via the rules option. The key is the rule's catch code (case-insensitive):

// Opt in to SQL-014 (missing RETURNING)
const result = analyze(sql, {
  rules: { 'sql-014': { enabled: true } },
});

// Disable SQL-007 for one call
const result = analyze(sql, {
  rules: { 'sql-007': { enabled: false } },
});

Use with...

Each example is a short, runnable integration showing how to wire the SDK into a common AI tool's pre-execution flow:

For in-editor feedback on sql`...` template literals (with --fix autofix), see the sibling package eslint-plugin-vibeguard.

Architecture, in one paragraph

The SDK parses your SQL with libpg-query, walks the resulting AST with a small, pure-function traversal helper, and runs each query through a registry of catch-functions. Each catch returns either null (didn't fire) or a structured Catch with code, severity, confidence, detail, and fix. No network calls. No state between calls. Sub-millisecond on typical queries. See ARCHITECTURE.md for the full design rationale.

Contributing

We welcome new catches that meet the SDK's scope: static-AST-detectable SQL anti-patterns with documented real-world incidents. The proposal process starts with an issue (template here); PRs come after maintainer feedback on whether the pattern fits.

See CONTRIBUTING.md for the full process, CODE_OF_CONDUCT.md for community expectations, and ARCHITECTURE.md for how the codebase is laid out.

Security

This SDK does static analysis. It does not execute SQL. It does not open network connections. It does not log to disk.

If you find a vulnerability — a false-negative that lets a real-world dangerous pattern through, a panic / crash on adversarial input, or a supply-chain concern — see SECURITY.md for the disclosure process. Do not file security issues as public GitHub issues.

License

Apache License 2.0 — see also NOTICE for attribution requirements that travel with derivative works.

About

VibeGuard is a wire-protocol security layer for AI agents that write SQL. This SDK is the open-source static-analysis component of the broader product.