Interlace ESLint
ESLint Interlace
PostgreSQLRules

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

On this page