Exploit Analysis: PostgreSQL COPY FROM Filesystem Access

A deep dive into PostgreSQL filesystem exploits. Learn how to engineer static analysis guards to prevent unauthorized database-level file access.

4 min read
Exploit Analysis: PostgreSQL COPY FROM Filesystem Access
Share:

When PostgreSQL reads your filesystem, your server is compromised. Here is the expliot analysis of COPY FROM and the static analysis standard to block unauthorized filesystem access.

PostgreSQL's COPY FROM is powerful. It can bulk load data from files.

It can also read /etc/passwd.

The Attack

javascript
// ❌ User controls file path
const filepath = req.body.filepath;
await client.query(`COPY users FROM '${filepath}'`);

Attacker input:

text
filepath: /etc/passwd

PostgreSQL now reads your system files into the database.

Security References

This vulnerability is well-documented in industry security standards:

StandardReferenceDescription
CWE-73External Control of File Name or PathApplication allows external input to control file paths
CWE-22Path TraversalImproper limitation of pathname to restricted directory
CVE-2019-9193PostgreSQL COPY FROM PROGRAMArbitrary code execution via COPY FROM PROGRAM (PostgreSQL 9.3-11.2)
OWASPA03:2021 InjectionInjection attacks including file path manipulation

⚠️ Note: While PostgreSQL considers CVE-2019-9193 a "feature" for superusers, the underlying pattern of user-controlled file paths in application code remains a critical vulnerability.

What Can Be Read

TargetImpact
/etc/passwdUser enumeration
/etc/shadowPassword hashes (if accessible)
Application config filesSecrets, database credentials
.env filesAll environment secrets
SSH keysServer access
Application source codeLogic, vulnerabilities

The Correct Pattern

javascript
// ✅ Never use user input in file paths
const ALLOWED_IMPORTS = {
  users: '/var/imports/users.csv',
  products: '/var/imports/products.csv',
};

const filepath = ALLOWED_IMPORTS[req.body.type];
if (!filepath) throw new Error('Invalid import type');

await client.query(`COPY users FROM '${filepath}'`);

// ✅ Or use COPY FROM STDIN with validated data
const stream = client.query(pgCopyStreams.from('COPY users FROM STDIN CSV'));
// Pipe validated CSV data to stream

COPY TO is Also Dangerous

javascript
// ❌ Attacker can write to filesystem
await client.query(`COPY users TO '/var/www/html/shell.php'`);

Combined with control over data, this enables:

  • Web shell deployment
  • Configuration file overwrite
  • Cron job injection

The Rule: pg/no-unsafe-copy-from

This pattern is detected by the pg/no-unsafe-copy-from rule from eslint-plugin-pg. The rule uses tiered detection:

Detection TypeSeverityTriggered By
Dynamic Path🔒 CRITICALTemplate literals with ${var}, string concatenation with variables
Hardcoded Path⚠️ MEDIUMLiteral file paths (operational risk, not injection)
STDIN✅ ValidCOPY FROM STDIN patterns

Let ESLint Catch This

bash
npm install --save-dev eslint-plugin-pg
javascript
import pg from 'eslint-plugin-pg';
export default [pg.configs.recommended];

Enable Only This Rule

javascript
import pg from 'eslint-plugin-pg';

export default [
  {
    plugins: { pg },
    rules: {
      'pg/no-unsafe-copy-from': 'error',
    },
  },
];

Configure for Admin Scripts

If you have legitimate admin/migration scripts that use hardcoded file paths:

javascript
export default [
  {
    files: ['**/migrations/**', '**/scripts/**'],
    rules: {
      'pg/no-unsafe-copy-from': ['error', { allowHardcodedPaths: true }],
    },
  },
];

Allow Specific Paths

javascript
export default [
  {
    rules: {
      'pg/no-unsafe-copy-from': [
        'error',
        { allowedPaths: ['^/var/imports/', '\\.csv$'] },
      ],
    },
  },
];

What You'll See

Dynamic Path (CRITICAL - Injection Risk)

bash
src/import.ts
  8:15  error  🔒 CWE-73 OWASP:A03-Injection | Dynamic file path in COPY FROM detected - potential arbitrary file read. | CRITICAL [SOC2,PCI-DSS]
                  Fix: Never use user input in COPY FROM paths. Use COPY FROM STDIN for user data.

Hardcoded Path (MEDIUM - Operational Risk)

bash
src/import.ts
  8:15  warning  ⚠️ CWE-73 | Hardcoded file path in COPY FROM - server-side file access. | MEDIUM
                    Fix: Prefer COPY FROM STDIN for application code. Use allowHardcodedPaths option if this is an admin script.

Before/After: Fixing the Lint Error

❌ Before (Triggers Lint Error)

javascript
// This code triggers pg/no-unsafe-copy-from
const filepath = req.body.filepath;
await client.query(`COPY users FROM '${filepath}'`);

✅ After (Lint Error Resolved)

javascript
// Use COPY FROM STDIN - the recommended safe pattern
import { from as copyFrom } from 'pg-copy-streams';
import { Readable } from 'stream';

async function importUsers(csvData) {
  const client = await pool.connect();
  try {
    // ✅ COPY FROM STDIN is safe - no file system access
    const stream = client.query(
      copyFrom('COPY users (name, email) FROM STDIN CSV'),
    );

    // Validate and stream the data from your application
    const validatedCsv = csvData
      .map((row) => `${sanitize(row.name)},${sanitize(row.email)}`)
      .join('\n');

    Readable.from(validatedCsv).pipe(stream);

    await new Promise((resolve, reject) => {
      stream.on('finish', resolve);
      stream.on('error', reject);
    });
  } finally {
    client.release();
  }
}

Key changes:

  • Replaced COPY FROM '/path/to/file' with COPY FROM STDIN
  • Data now flows through your application, not the filesystem
  • You control validation before it reaches the database

Quick Install

bash
npm install --save-dev eslint-plugin-pg
javascript
import pg from 'eslint-plugin-pg';
export default [pg.configs.recommended];

Keep PostgreSQL in the database, not in your filesystem.


📦 npm: eslint-plugin-pg 📖 Rule docs: no-unsafe-copy-from


The Interlace ESLint Ecosystem Interlace is a high-fidelity suite of static code analyzers designed to automate security, performance, and reliability for the modern Node.js stack. With over 330 rules across 18 specialized plugins, it provides 100% coverage for OWASP Top 10, LLM Security, and Database Hardening.

Explore the full Documentation

© 2026 Ofri Peretz. All rights reserved.


Build Securely. I'm Ofri Peretz, a Security Engineering Leader and the architect of the Interlace Ecosystem. I build static analysis standards that automate security and performance for Node.js fleets at scale.

ofriperetz.dev | LinkedIn | GitHub

Built with Nuxt UI • © 2026 Ofri Peretz