Skip to content

Releases: flvmnt/pgfence

v0.4.1: Trust Contract Hardening

16 Mar 10:26

Choose a tag to compare

Trust Contract

  • 18 silent failure paths eliminated in Knex (6) and Sequelize (12) transpilers: every early return that silently dropped migration statements now emits an ExtractionWarning
  • Plugin crashes surfaced in structured output (JSON, GitHub, SARIF), not just stderr
  • Coverage % fixed: new unanalyzable flag on ExtractionWarning distinguishes truly unanalyzable statements from informational warnings

Bug Fixes

  • Trace mode DB connection leak: close clients in finally block
  • Policy ignore bleed: fileIgnoredRules restricted to first statement only
  • lock-timeout-after-dangerous-statement now suppressible via inline ignore
  • Stale adjustedRisk cleared on trace-merge mismatch override
  • NaN guard on --max-lock-timeout / --max-statement-timeout
  • Stats file and package.json errors include file path and message

LSP

  • Format auto-detection failure emits warning instead of silent fallback
  • Analysis crash clears stale diagnostics
  • Config fetch logs non-capability errors

Docs

  • 11 stale comments fixed, verified against PostgreSQL documentation

Tests

  • 393 tests (was 371)
  • 10 SARIF reporter tests (was zero)
  • DROP SCHEMA, DROP SCHEMA CASCADE, DROP CONSTRAINT tests
  • 6 adjustRisk boundary tests (exact thresholds)
  • REFRESH MATERIALIZED VIEW WITH NO DATA test
  • 2 coverage calculation accuracy tests

Full changelog: https://github.com/flvmnt/pgfence/blob/main/CHANGELOG.md

v0.4.0: Trace Mode

14 Mar 16:07

Choose a tag to compare

Trace Mode

Run your migrations against a real Postgres instance and verify every lock prediction:

pgfence trace migrations/*.sql

Spins up a disposable Docker container, executes each statement, queries pg_locks + system catalog, and compares what actually happened against static analysis predictions. Every check gets a verification status: confirmed, mismatch, trace-only, static-only, or error.

Highlights

  • Observer polling for CONCURRENTLY: opens a second connection that polls pg_locks while the main connection runs the statement, capturing transient locks that other tools skip
  • Catalog diffing: detects table rewrites (relfilenode changes), column modifications, constraint validation state, and index creation
  • Custom images: --docker-image postgis/postgis:17 for extensions
  • Version targeting: --pg-version 14 to test against your production version
  • CI mode: --ci fails on mismatches, execution errors, or risk threshold violations

Security

  • Credential sanitization covers both postgres:// and postgresql:// URL schemes
  • Plugin loader rejects paths outside the project directory
  • Container: random password, 127.0.0.1 only, cleaned up on crash

Trust Contract Fixes

  • trace-only findings: risk derived from observed lock mode (was hard-coded LOW)
  • mismatch findings: risk upgraded to max(static, traced) when trace is stronger
  • CI now fails on execution errors, not just mismatches

Rules

  • Default minimum PostgreSQL version bumped from 11 to 14
  • rename-column suppresses expand/contract recipe on PG14+ (instant)
  • Removed stale PG10/PG12 references from safe rewrites

Stats

  • 371 tests (was 176)
  • 3,400+ lines added across 32 files

Full changelog: https://github.com/flvmnt/pgfence/blob/main/CHANGELOG.md

v0.3.2

09 Mar 09:53

Choose a tag to compare

What's Changed

Bug Fixes

  • Coverage formula: reports 0% when parse errors prevent analysis (was falsely reporting 100%, Trust Contract violation)
  • Safe rewrite recipes: SET NOT NULL migration boundary comments (PG12+), ADD COLUMN NOT NULL + volatile default appends constraint steps, smallserial maps to smallint
  • LSP server: wrap workspace/configuration in try/catch (prevents crash on Neovim/Helix), code action safe rewrite trailing newline
  • Policy checks: REINDEX TABLE no longer classified as ACCESS EXCLUSIVE (takes SHARE lock on table), DROP TRIGGER now tracked for wide-lock-window detection, pgfence-ignore comments suppress file-level policy violations
  • RENAME TABLE recipe: corrected view note (simple views ARE auto-updatable in PostgreSQL)

Security

  • FK action allowlist: knex/sequelize transpilers now validate onDelete/onUpdate values against an allowlist (CASCADE, RESTRICT, NO ACTION, SET NULL, SET DEFAULT), preventing crafted migration files from injecting extra statements into the analysis pipeline
  • Sequelize.literal(): no longer interpolates user content into synthetic SQL

Cleanup

  • pg moved to optionalDependencies (only needed for --db-url)
  • Removed dead code (src/git-diff.ts)
  • Deduplicated RISK_ORDER (LSP imports from analyzer instead of redeclaring)
  • --format help text now lists drizzle and sequelize

v0.3.1

06 Mar 17:01

Choose a tag to compare

Bug fixes

  • CREATE INDEX safe rewrite: Quick fix no longer uses (...) placeholder for columns. Now derives the safe SQL from the original statement, preserving columns, WHERE clause, UNIQUE keyword, and USING clause.
  • LSP initializationOptions: Server now reads config from initializationOptions at startup instead of using defaults until the first workspace/didChangeConfiguration event.

Docs

  • CLAUDE.md: added LSP server and VS Code extension architecture section.
  • Removed em dashes from CLAUDE.md.

v0.3.0

06 Mar 14:07

Choose a tag to compare

VS Code Extension

pgfence now runs inside your editor. Install from the VS Code Marketplace or search "pgfence" in the Extensions panel.

  • Inline diagnostics: lock modes, risk levels, and policy violations as you type
  • Quick fixes: one-click safe rewrite replacements (lightbulb menu)
  • Hover info: lock mode, blocked operations, and safe alternatives
  • Status bar: error/warning count for the current file

The extension is a thin client that discovers @flvmnt/pgfence from your project's node_modules. One source of truth for CLI and editor.

LSP Server

New pgfence lsp command starts the Language Server Protocol server over stdio. The VS Code extension uses this, but any LSP-compatible editor can too.

New Rules

Rule Risk Description
ban-char-field LOW Flag char(n)/character(n), prefer text
prefer-identity LOW Flag serial/bigserial, prefer IDENTITY (PG10+)
drop-database CRITICAL DROP DATABASE is irreversible
ban-alter-domain-add-constraint HIGH Validates against all tables using the domain
ban-create-domain-with-constraint LOW Domains with constraints have poor migration support

Parser Improvements

  • Character-level source offsets (startOffset/endOffset) with correct UTF-8 byte-to-char conversion
  • Policy violations now carry statementIndex for precise source mapping

Stats

  • 42 check patterns (up from 37)
  • 306 tests (up from 286)
  • 54 items across 4 risk levels on the homepage

Install

npm install -D @flvmnt/pgfence@0.3.0
code --install-extension flvmnt.pgfence

Full Changelog: v0.2.4...v0.3.0

v0.2.4

05 Mar 14:34

Choose a tag to compare

What's new

  • fix: Regenerate lockfile after optionalDependencies change (CI fix)
  • fix: GitHub PR reporter URL and action.yml format list
  • fix: Extractor silent failures in Knex and Sequelize transpilers
  • fix: Remove em dashes from all user-visible messages
  • fix: Correct DROP INDEX CONCURRENTLY and lock_timeout=0 in policy checks
  • fix: Correct lock modes for FK, ATTACH PARTITION, and REINDEX
  • chore: Move typescript-estree to optionalDependencies

v0.2.3

03 Mar 08:39

Choose a tag to compare

Open Source (MIT License)

pgfence is now fully open source under the MIT license, replacing the previous FSL-1.1-MIT.

Changes since v0.2.2

  • License changed from FSL-1.1-MIT to MIT
  • Updated all documentation and badges to reflect MIT license
  • Added pgfence.com domain rule to project guidelines

This release includes all the ORM extractor improvements from v0.2.2:

  • Knex: .alter() modifier, setNullable/dropNullable, references/inTable chain, timestamps, identifier quoting
  • Sequelize: up-only filtering, addConstraint/removeConstraint, addIndex options (concurrently, unique), literal() detection
  • TypeORM: builder API detection with 40+ methods, auto-commit detection
  • 167 tests, all passing

v0.2.2

02 Mar 15:24

Choose a tag to compare

ORM Extractor Overhaul

Comprehensive audit and fix of all 6 ORM extractors (TypeORM, Knex, Sequelize, Prisma, Drizzle, raw SQL), cross-referenced against official ORM documentation.

Knex

  • Fix references().inTable() chain producing invalid SQL
  • Fix unescaped single quotes in DEFAULT values
  • Add createTableIfNotExists, dropTableIfExists, table() alias support
  • Add timestamps() column generation (created_at, updated_at)
  • Add .alter() modifier: emits ALTER COLUMN TYPE instead of ADD COLUMN
  • Add setNullable()/dropNullable() support
  • Add dropColumns() plural form
  • Double-quote all identifiers to handle reserved words

Sequelize

  • Filter to up() method only (was incorrectly analyzing down())
  • Add addConstraint/removeConstraint support (UNIQUE, FK, CHECK)
  • Add onDelete/onUpdate support on constraints and column-level references
  • Parse addIndex options: concurrently, unique, name
  • Detect Sequelize.literal() as volatile default
  • Fix unescaped single quotes in DEFAULT values
  • Thread filePath through all transpile functions
  • Double-quote all identifiers

TypeORM

  • Detect and warn on builder API usage (createTable, addColumn, etc.)
  • Expand from 13 to 40+ builder methods (dropTable, clearTable, check/exclusion constraints, views, schemas, all plural variants)
  • Fix addUniqueConstraint to createUniqueConstraint (matching actual API)

Testing

  • 167 tests (up from 153), all passing
  • 14 new fixture files covering every fix

v0.2.1

25 Feb 22:42

Choose a tag to compare

Security

  • Fix shell injection in GitHub Action — all inputs now passed through env vars and bash array, eliminating injection vectors and fixing paths with spaces

Bug Fixes

  • Correct lock mode for REFRESH MATERIALIZED VIEW CONCURRENTLY — was EXCLUSIVE, now correctly SHARE UPDATE EXCLUSIVE per PostgreSQL docs (allows reads AND writes)
  • Eliminate silent failures in plugin system — plugin errors logged to stderr instead of silently swallowed
  • Eliminate silent failures in extractors — TypeORM now matches queryRunner.manager.query(), Knex/Sequelize transpilers emit warnings for dynamic table names instead of returning empty results
  • Add missing rule coverage — default case for unknown constraint types, DROP SCHEMA CASCADE detection, DropStmt/TruncateStmt/RenameStmt added to lock tracking
  • Per-file parse error handling — a syntax error in one file no longer aborts analysis of the entire batch
  • maxRisk includes policy violations — error-level policy violations now reflected in maxRisk (was showing SAFE with error-level violations)
  • SARIF coverage summary — Trust Contract compliance: SARIF output now includes coverage stats
  • Error handling improvements — start-after only catches ENOENT, cloud hooks always log errors, stats file schema validated, config JSON validated

Chores

  • Exclude tests/cloud/ from vitest config
  • Add @iarna/toml as optional dependency
  • Fix duplicate drizzle detection condition
  • Flag CreateProcedureStmt as unanalyzable

v0.2.0

25 Feb 18:39

Choose a tag to compare

What's new

5 new DDL rules

  • ALTER TYPE ... ADD VALUE (enum evolution)
  • ATTACH/DETACH PARTITION
  • REFRESH MATERIALIZED VIEW
  • REINDEX TABLE/INDEX/SCHEMA/DATABASE
  • CREATE/DROP/ENABLE/DISABLE TRIGGER

Expanded policy checks

  • Lock timeout ordering validation (must come before DDL)
  • Wide lock window detection
  • Savepoint/rollback awareness
  • Timeout-too-permissive warnings

ORM extractor improvements

  • Knex: builder chain extraction (table.string(), table.integer(), etc.)
  • Knex: conditional migration support
  • TypeORM: conditional queryRunner.query() extraction
  • Sequelize: queryInterface method extraction

New CLI flags

  • --pg-version — tune analysis for your Postgres version (11–17)
  • --stats-file — provide table stats JSON for size-aware risk scoring

Other

  • Plugin system for custom rules
  • Schema snapshot support
  • Transaction state tracking
  • 48 new tests (153 total)

Full Changelog: v0.1.5...v0.2.0