HIGH command injectionfeathersjs

Command Injection in Feathersjs

How Command Injection Manifests in Feathersjs

Command injection vulnerabilities in Feathersjs applications typically arise when user-controlled data flows directly into system commands without proper sanitization. Feathersjs, being a lightweight Node.js framework, doesn't inherently protect against command injection—developers must implement these safeguards themselves.

The most common pattern occurs in Feathersjs services that use Node.js's child_process module to execute shell commands. Consider this vulnerable service implementation:

const { exec } = require('child_process');

class FileService {
  async getFileMetadata(context) {
    const { filename } = context.params.query;
    
    // VULNERABLE: Direct interpolation of user input
    const command = `file -b "${filename}"`;
    
    return new Promise((resolve, reject) => {
      exec(command, (error, stdout, stderr) => {
        if (error) return reject(error);
        resolve({ metadata: stdout.trim() });
      });
    });
  }
}

module.exports = FileService;

In this example, an attacker could pass filename=test.txt; rm -rf / as the query parameter, causing the shell to execute both the file command and the destructive rm command.

Another Feathersjs-specific scenario involves using the app.setup() method or custom hooks that execute shell commands during service initialization or data processing. For instance:

const { execSync } = require('child_process');

class ImportService {
  async importData(context) {
    const { filePath } = context.data;
    
    // VULNERABLE: execSync with interpolated user input
    const output = execSync(`python3 import_script.py "${filePath}"`);
    
    return { success: true, output: output.toString() };
  }
}

Feathersjs's event-driven architecture can also create command injection opportunities when event data is used in shell commands. A hook that processes events might look like:

const { exec } = require('child_process');

module.exports = function() {
  return async (context) => {
    const { type, payload } = context.dispatch;
    
    // VULNERABLE: Event data used in shell command
    if (type === 'process_file') {
      exec(`process_file.sh "${payload.filePath}"`);
    }
  };
};

The framework's flexibility with service adapters can also lead to command injection when integrating with external systems. For example, a custom database adapter might use shell commands for data operations:

class ShellDBAdapter {
  constructor(config) {
    this.config = config;
  }
  
  async find(params) {
    // VULNERABLE: Query parameters interpolated into shell command
    const query = params.query || {};
    const command = `sqlite3 ${this.config.dbPath} "SELECT * FROM ${query.table || 'default'}"`;
    
    return new Promise((resolve, reject) => {
      exec(command, (error, stdout) => {
        if (error) return reject(error);
        resolve(JSON.parse(stdout));
      });
    });
  }
}

These patterns are particularly dangerous in Feathersjs because the framework's convention-over-configuration approach means developers often write custom code without built-in security protections. The framework provides no automatic escaping or validation for shell commands, leaving this responsibility entirely to the developer.

Feathersjs-Specific Detection

Detecting command injection vulnerabilities in Feathersjs applications requires a combination of static code analysis and dynamic testing. For static analysis, look for these patterns in your codebase:

// Patterns to search for:
// 1. Direct use of exec/execSync with string interpolation
const { exec } = require('child_process');
exec(`command ${userInput}`);

// 2. execSync with concatenated strings
execSync('command ' + userInput);

// 3. exec with template literals containing variables
exec(`command ${variable}`);

// 4. exec with path traversal vulnerabilities
exec(`ls ${__dirname}/${userInput}`);

Using middleBrick's CLI tool, you can scan your Feathersjs API endpoints for command injection vulnerabilities:

npm install -g middlebrick
middlebrick scan https://your-feathers-api.com

middleBrick performs black-box scanning that tests for command injection by sending payloads designed to trigger shell command execution. The scanner tests for common injection patterns including:

  • Command chaining with semicolons, ampersands, and pipes
  • Background execution with ampersands
  • File redirection with greater/less than symbols
  • Command substitution with backticks and $()

For Feathersjs applications specifically, middleBrick's scanner examines:

# Scan specific Feathersjs endpoints
middlebrick scan https://api.example.com/messages
middlebrick scan https://api.example.com/users

# Scan with detailed output
middlebrick scan https://api.example.com --output json --verbose

The scanner's findings include severity ratings based on the potential impact and likelihood of exploitation. For command injection, middleBrick tests the unauthenticated attack surface, which is particularly relevant for Feathersjs APIs that might expose administrative endpoints without proper authentication.

middleBrick's LLM/AI security module also checks for AI-specific command injection scenarios, such as when user prompts are passed to shell commands in AI-powered Feathersjs services. This includes testing for:

  • System prompt leakage that could reveal command injection vulnerabilities
  • Prompt injection that modifies shell commands
  • AI agent tool execution that might invoke shell commands

For continuous monitoring, the middleBrick GitHub Action can be configured to scan your Feathersjs API in CI/CD pipelines:

name: API Security Scan
on: [push, pull_request]

jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run middleBrick Scan
        run: |
          npx middlebrick scan https://staging.your-feathers-app.com
        continue-on-error: true
      - name: Fail on High Risk
        if: failure()
        run: exit 1

This ensures that any command injection vulnerabilities introduced in new code are caught before deployment to production.

Feathersjs-Specific Remediation

Remediating command injection vulnerabilities in Feathersjs requires both immediate fixes and architectural changes to prevent future issues. The primary defense is eliminating shell command execution where possible and using safer alternatives.

For file operations that might currently use shell commands, use Node.js's built-in fs module:

const fs = require('fs').promises;

class SafeFileService {
  async getFileMetadata(context) {
    const { filename } = context.params.query;
    
    // Validate filename: allow only alphanumeric, hyphen, underscore, dot
    if (!/^[a-zA-Z0-9_\.\-]+$/.test(filename)) {
      throw new Error('Invalid filename');
    }
    
    const filePath = path.join(__dirname, '../uploads', filename);
    
    // Use safe fs operations instead of shell commands
    const stats = await fs.stat(filePath);
    const fileBuffer = await fs.readFile(filePath);
    
    return {
      size: stats.size,
      created: stats.birthtime,
      modified: stats.mtime,
      contentLength: fileBuffer.length
    };
  }
}

When shell commands are unavoidable, use the child_process.execFile method instead of exec, as it doesn't use a shell and prevents command injection:

const { execFile } = require('child_process');

class SafeExecService {
  async getFileMetadata(context) {
    const { filename } = context.params.query;
    
    // Validate and sanitize input
    if (!/^[a-zA-Z0-9_\.\-]+$/.test(filename)) {
      throw new Error('Invalid filename');
    }
    
    return new Promise((resolve, reject) => {
      // execFile uses arguments array, no shell interpretation
      execFile('file', ['-b', filename], (error, stdout) => {
        if (error) return reject(error);
        resolve({ metadata: stdout.trim() });
      });
    });
  }
}

For database operations that might use shell commands, use proper database libraries:

const sqlite3 = require('sqlite3').verbose();

class SafeDBService {
  constructor() {
    this.db = new sqlite3.Database('./data.db');
  }
  
  async find(params) {
    const query = params.query || {};
    
    // Validate table name against whitelist
    const allowedTables = ['users', 'messages', 'files'];
    const tableName = allowedTables.includes(query.table) ? query.table : 'users';
    
    // Use parameterized queries
    return new Promise((resolve, reject) => {
      this.db.all(
        `SELECT * FROM ${tableName} WHERE id = ?`,
        [query.id],
        (err, rows) => {
          if (err) return reject(err);
          resolve(rows);
        }
      );
    });
  }
}

Implement comprehensive input validation using a whitelist approach:

const validateInput = (input, type) => {
  const validators = {
    filename: /^[a-zA-Z0-9_\.\-]+$/,
    userId: /^[0-9]+$/,
    email: /^[^@\s]+@[^@\s]+\.[^@\s]+$/,
    path: /^([a-zA-Z0-9_\-\/]+\.)?[a-zA-Z0-9_\-]+$/,
  };
  
  if (!validators[type]) {
    throw new Error(`No validator for type: ${type}`);
  }
  
  return validators[type].test(input);
};

// Usage in a Feathersjs hook
module.exports = function() {
  return async (context) => {
    const { filename } = context.params.query;
    
    if (!validateInput(filename, 'filename')) {
      throw new Error('Invalid filename format');
    }
    
    return context;
  };
};

Create a security middleware for your Feathersjs app to centralize validation:

const securityMiddleware = async (context, next) => {
  const { method, type, params, data } = context;
  
  // Block dangerous characters in query parameters
  if (params.query) {
    for (const [key, value] of Object.entries(params.query)) {
      if (typeof value === 'string' && /[;|&$`\n\r\t]/.test(value)) {
        throw new Error(`Potential injection in parameter: ${key}`);
      }
    }
  }
  
  // Validate data payload for create/update operations
  if (data && method === 'create') {
    // Add your validation logic here
    if (data.command) {
      throw new Error('Command field not allowed');
    }
  }
  
  await next();
};

// Apply globally
app.hooks({
  before: {
    all: [securityMiddleware]
  }
});

For Feathersjs services that must interact with external systems, use the framework's built-in validation and sanitization features:

const { BadRequest } = require('@feathersjs/errors');

class SecureService {
  async create(data, params) {
    // Validate data structure
    if (!data || typeof data !== 'object') {
      throw new BadRequest('Invalid data format');
    }
    
    // Check for dangerous fields
    const dangerousFields = ['exec', 'execSync', 'spawn', 'execFile'];
    for (const field of dangerousFields) {
      if (field in data) {
        throw new BadRequest(`Field ${field} is not allowed`);
      }
    }
    
    // Proceed with safe operations
    return this._safeCreate(data, params);
  }
}

Finally, implement comprehensive logging and monitoring to detect attempted command injection attacks:

const winston = require('winston');

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'security.log' })
  ]
});

// Security monitoring hook
module.exports = function() {
  return async (context) => {
    const { method, params, data } = context;
    
    // Log suspicious patterns
    if (params.query) {
      for (const [key, value] of Object.entries(params.query)) {
        if (typeof value === 'string' && /[;|&$`\n\r\t]/.test(value)) {
          logger.warn('Suspicious query parameter detected', {
            service: context.path,
            method,
            key,
            value,
            timestamp: new Date().toISOString(),
            ip: params.provider === 'rest' ? params.headers['x-forwarded-for'] : 'unknown'
          });
        }
      }
    }
    
    return context;
  };
};

Related CWEs: inputValidation

CWE IDNameSeverity
CWE-20Improper Input Validation HIGH
CWE-22Path Traversal HIGH
CWE-74Injection CRITICAL
CWE-77Command Injection CRITICAL
CWE-78OS Command Injection CRITICAL
CWE-79Cross-site Scripting (XSS) HIGH
CWE-89SQL Injection CRITICAL
CWE-90LDAP Injection HIGH
CWE-91XML Injection HIGH
CWE-94Code Injection CRITICAL

Frequently Asked Questions

How can I test my Feathersjs API for command injection vulnerabilities?
Use middleBrick's CLI tool to scan your API endpoints: npx middlebrick scan https://your-feathers-api.com. The scanner tests for command injection by sending payloads with shell metacharacters like semicolons, pipes, and backticks. It also checks for LLM-specific injection scenarios if your Feathersjs service includes AI features. For continuous testing, add the middleBrick GitHub Action to your CI/CD pipeline to automatically scan before deployment.
What's the difference between exec and execFile in Node.js, and why does it matter for Feathersjs security?
exec uses a shell to interpret the command string, making it vulnerable to command injection if user input is interpolated. execFile executes a file directly with arguments as an array, bypassing the shell entirely. In Feathersjs applications, always prefer execFile when you need to run external commands. For example, use execFile('file', ['-b', filename]) instead of exec(`file -b "${filename}"`). This simple change eliminates the command injection vulnerability while maintaining the same functionality.