Skip to main content
ESLint Interlace
Plugin: pgRules

no-unsafe-copy-from

Prevents COPY FROM with file paths (should use STDIN for safe client-side data loading).

Keywords: COPY FROM, file access, CWE-73, pg, node-postgres, security, path injection

Prevents COPY FROM with file paths (should use STDIN for safe client-side data loading).

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

Quick Summary

AspectDetails
CWE ReferenceCWE-73 (External Control of File Name)
OWASPA03:2021 Injection
SeverityCRITICAL (dynamic) / MEDIUM (hardcoded)
CategorySecurity
FixableNo

Rule Details

COPY FROM '/path/to/file' reads files from the server filesystem, not the client. If the path is user-controlled, attackers can read arbitrary files like /etc/passwd, .env, or application secrets.

This rule uses tiered detection:

  • CRITICAL: Dynamic paths (template literals with expressions, string concatenation with variables)
  • MEDIUM: Hardcoded paths (still risky, but not injection)

❌ Incorrect (CRITICAL - Dynamic Path)

// User-controlled path - arbitrary file read vulnerability
const filePath = req.body.filepath;
await client.query(`COPY users FROM '${filePath}'`);

// String concatenation with variable
await pool.query('COPY users FROM ' + userPath);

// Template literal with expression
await client.query(`COPY ${table} FROM '${path}'`);

⚠️ Incorrect (MEDIUM - Hardcoded Path)

// Server-side file access - not injection, but operational risk
await client.query("COPY users FROM '/var/data/users.csv'");

// Still flagged - prefer STDIN for application code
await pool.query("COPY logs FROM '/tmp/import.csv' CSV");

✅ Correct

// COPY FROM STDIN - data flows through your application
import { from as copyFrom } from 'pg-copy-streams';

const stream = client.query(copyFrom('COPY users FROM STDIN CSV'));
fs.createReadStream('data.csv').pipe(stream);

// With validation
const stream = client.query(
  copyFrom('COPY users (id, name) FROM STDIN WITH (FORMAT CSV, HEADER)'),
);
Readable.from(validatedCSV).pipe(stream);

Options

allowHardcodedPaths

Type: boolean Default: false

Allow hardcoded file paths. Use for admin scripts or migrations where server-side file access is intentional.

// eslint.config.js
export default [
  {
    rules: {
      'pg/no-unsafe-copy-from': ['error', { allowHardcodedPaths: true }],
    },
  },
];

allowedPaths

Type: string[] (regex patterns) Default: []

Allow specific file paths matching regex patterns.

// eslint.config.js
export default [
  {
    rules: {
      'pg/no-unsafe-copy-from': [
        'error',
        {
          allowedPaths: [
            '^/var/imports/', // Allow /var/imports/* directory
            '\\.csv$', // Allow any .csv file
          ],
        },
      ],
    },
  },
];

Error Message Format

Dynamic Path (CRITICAL)

🔒 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. | https://www.postgresql.org/docs/current/sql-copy.html

Hardcoded Path (MEDIUM)

⚠️ 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. | https://www.postgresql.org/docs/current/sql-copy.html

When Not To Use It

  • For internal admin tools with validated file paths on trusted servers (use allowHardcodedPaths: true)
  • In migration scripts running on controlled infrastructure
  • When paths are from a known-safe allowlist (use allowedPaths option)

Known False Negatives

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

Query from Variable

Why: Query strings in variables are not analyzed.

// ❌ NOT DETECTED - Query stored in variable
const copyQuery = `COPY users FROM '${userPath}'`;
await client.query(copyQuery);

Mitigation: Use inline query strings. Prefer COPY FROM STDIN pattern.

Wrapped Query Methods

Why: Wrapper functions hide query contents.

// ❌ NOT DETECTED - Wrapped query execution
async function executeQuery(sql: string) {
  return client.query(sql);
}
await executeQuery(`COPY data FROM '${path}'`);

Mitigation: Apply rule to all modules. Use dedicated import utilities.

ORM Raw Query Methods

Why: ORM-specific query methods may not be tracked.

// ❌ NOT DETECTED - ORM raw query
await sequelize.query(`COPY users FROM '${path}'`, { raw: true });

Mitigation: Apply rule to ORM raw query patterns. Configure additional query method names.

COPY FROM PROGRAM

Why: Rule focuses on file paths; PROGRAM variant has different syntax.

// ❌ NOT DETECTED - COPY FROM PROGRAM is equally dangerous
await client.query(`COPY users FROM PROGRAM '${cmd}'`);

Mitigation: Add custom rule for COPY FROM PROGRAM detection. Security review for all COPY commands.

References

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.