All rules Rule BC005 · PC
Credential rule · any database
warning
PC · Privileges & credentials
free in the CLI

Should a column named password or token get a literal value in a migration?

Secret-named column given a literal value

Warning: the name is the evidence: very likely a secret, reported so a human can decide.

What happens

A column called password, api_key, secret or token that receives a literal in a DEFAULT, an INSERT or an UPDATE is a credential written into the migration by hand, the seeded admin account with password 'admin', the api_key DEFAULT 'changeme' that ships to every row. The name is the evidence, so this is a warning rather than a certainty: the rule stays silent on hashed values (bcrypt, argon2, SCRAM, pbkdf2), on empty strings and placeholders, and on names that merely contain the word (password_hash, token_type, api_key_id).

Why it is dangerous on a populated table

Table size does not change this one: a committed credential is live from the first row, and git history keeps it after the fix.

Fires on

INSERT INTO users (email, password) VALUES ('admin@example.com', 'admin123');

The safe pattern

Seed a hash, never the plaintext (and let the application own password hashing); for tokens and keys, seed nothing, or a placeholder the deploy substitutes, and have the value set at runtime. A DEFAULT on a secret column should be NULL or an empty string.

INSERT INTO users (email, password_hash) VALUES ('admin@example.com', '$2b$12$C6UzMDM.H6dfI/f/IKcEeO9rOsTbnR6Q3oFf9zGjHpMqU9ZAwK9Tu');

Fixtures

The rule ships with these files and the test suite runs them on every change: the first set must fire, the second must stay silent.

Fires (4)

create table default token
CREATE TABLE webhooks (id bigint PRIMARY KEY, url text NOT NULL, signing_token text DEFAULT 'shared-token');
default api key
ALTER TABLE tenants ADD COLUMN api_key text NOT NULL DEFAULT 'changeme';
seed plaintext password
INSERT INTO users (email, password) VALUES ('admin@example.com', 'admin123');
update client secret
UPDATE oauth_clients SET client_secret = 'local-dev-secret' WHERE name = 'dashboard';

Stays silent (5)

bcrypt hash
INSERT INTO users (email, password) VALUES ('admin@example.com', '$2b$12$C6UzMDM.H6dfI/f/IKcEeO9rOsTbnR6Q3oFf9zGjHpMqU9ZAwK9Tu');
column name lookalikes
ALTER TABLE users ADD COLUMN password_hash text DEFAULT 'pending';
ALTER TABLE oauth_clients ADD COLUMN token_type text DEFAULT 'bearer';
INSERT INTO api_keys (api_key_id, label) VALUES ('k_01', 'primary');
empty and null defaults
ALTER TABLE tenants ADD COLUMN api_key text DEFAULT '';
ALTER TABLE tenants ADD COLUMN webhook_secret text DEFAULT NULL;
insert without column list
INSERT INTO users VALUES (1, 'admin@example.com', 'admin123');
placeholder
INSERT INTO users (email, password) VALUES ('admin@example.com', '${ADMIN_PASSWORD}');

How to check locally

Catch this before it ships

This rule runs locally in the free CLI, or with the full corpus through the hosted service. No install, nothing leaves your machine:

npx bolvrk check migration.sql