Skip to main content
interlace
Plugin: pgRules

no-hardcoded-credentials

Prevents hardcoded passwords and connection strings in PostgreSQL client initialization.

CWE: CWE-522

⚠️ This rule errors by default in the recommended config.

Quick Summary

AspectDetails
CWE ReferenceCWE-798 (Hardcoded Credentials)
SeverityHigh (CVSS: 7.5)
CategorySecurity

Rule Details

Hardcoded credentials in source code can be exposed through version control, logs, or error messages.

❌ Incorrect

// Hardcoded password
const client = new Client({
  password: 'supersecret123',
});

// Hardcoded connection string
const pool = new Pool('postgres://user:password@localhost/db');

✅ Correct

// Environment variables
const client = new Client({
  password: process.env.PG_PASSWORD,
});

// Connection string from environment
const pool = new Pool(process.env.DATABASE_URL);

// Config file (not committed to VCS)
const config = require('./config.local.json');
const client = new Client(config.database);

Error Message Format

The rule provides LLM-optimized error messages (Compact 2-line format) with actionable security guidance:

🔒 CWE-798 OWASP:A04 CVSS:9.8 | Hardcoded Credentials detected | CRITICAL [SOC2,PCI-DSS,HIPAA,GDPR,ISO27001,NIST-CSF]
   Fix: Review and apply the recommended fix | https://owasp.org/Top10/A04_2021/

Message Components

ComponentPurposeExample
Risk StandardsSecurity benchmarksCWE-798 OWASP:A04 CVSS:9.8
Issue DescriptionSpecific vulnerabilityHardcoded Credentials detected
Severity & ComplianceImpact assessmentCRITICAL [SOC2,PCI-DSS,HIPAA,GDPR,ISO27001,NIST-CSF]
Fix InstructionActionable remediationFollow the remediation steps below
Technical TruthOfficial referenceOWASP Top 10

Known False Negatives

The following patterns are not detected due to static analysis limitations:

Template Literals with Credentials

Why: The rule only checks Literal nodes, not template literals.

// ❌ NOT DETECTED
const client = new Client({
  password: `supersecret123`, // Template literal, not string literal
});

Configs from packages or path aliases

Why: Configs and config factories in the same file, in RELATIVE modules (./config), and in relative JSON files (./database.json) are read. One from an npm package or a path alias (@/config) is not.

// ❌ NOT DETECTED
import { getConfig } from '@/config';
const client = new Client(getConfig());

Also checked (since 2026-10)

  • A postgres://user:password@host DSN anywhere in the file, not only in a constructor — the config module that holds it is the usual place. Loopback hosts (localhost, 127.0.0.1, [::1]) are skipped.
  • postgres.js postgres(url | options) and pg-promise pgp(options).

Spread Operator

Why: The rule iterates over properties, not spread sources.

// ❌ NOT DETECTED
const secrets = { password: 'hardcoded' };
const client = new Client({ ...secrets });

Variable References

Why: Values stored in variables aren't traced to their literal origin.

// ❌ NOT DETECTED
const password = 'supersecret123';
const client = new Client({ password });

Workaround: Use secret scanning tools (e.g., gitleaks, truffleHog) in CI/CD.

TypeORM Configs Belong to typeorm-security

A postgres URL inside a TypeORM options object — new DataSource({ type: 'postgres', url }), TypeOrmModule.forRoot({ … }), an ormconfig export — is not reported here. It is typeorm-security/no-hardcoded-credentials's finding, so a project running both plugins gets one report per secret, not two. The object counts as TypeORM's when it has a postgres-family type (postgres, aurora-postgres, cockroachdb) and the file imports typeorm / @nestjs/typeorm or the object carries TypeORM keys (entities, synchronize, migrations, subscribers, migrationsRun). Enable typeorm-security to keep coverage for those configs.

When Not To Use It

  • In test files with fixture data
  • In documentation examples
  • no-insecure-ssl (planned) - Prevents insecure SSL settings

Use this rule

npm install -D oxlint-plugin-postgresql-security
.oxlintrc.json
{  "jsPlugins": ["oxlint-plugin-postgresql-security"],  "rules": {    "postgresql-security/no-hardcoded-credentials": "error"  }}
vite.config.ts
import { defineConfig } from 'vite-plus';export default defineConfig({  lint: {    jsPlugins: ['oxlint-plugin-postgresql-security'],    rules: {      'postgresql-security/no-hardcoded-credentials': 'error',    },  },});

oxlint-plugin-postgresql-security re-exports eslint-plugin-postgresql-security/oxlint: the same rules under the same IDs, and no ESLint install. Why oxlint.

Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.