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
| Aspect | Details |
|---|---|
| CWE Reference | CWE-73 (External Control of File Name) |
| OWASP | A03:2021 Injection |
| Severity | CRITICAL (dynamic) / MEDIUM (hardcoded) |
| Category | Security |
| Fixable | No |
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.htmlHardcoded 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.htmlWhen 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
allowedPathsoption)
Related Rules
- no-unsafe-query - SQL injection prevention
- no-unsafe-search-path - Search path injection
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.