HIGH zip slipfeathersjs

Zip Slip in Feathersjs

How Zip Slip Manifests in Feathersjs

Zip Slip is a directory traversal vulnerability that occurs when an application extracts ZIP archives without validating file paths. In Feathersjs applications, this typically manifests through file upload endpoints that process user-supplied archives.

The vulnerability allows attackers to craft ZIP files with malicious paths like ../../etc/passwd or ../../../../tmp/evil.sh. When Feathersjs extracts these archives using Node.js's built-in zlib or adm-zip libraries without path validation, files are written outside the intended directory.

Common Feathersjs attack vectors include:

  • File upload services using multer or custom middleware that accept ZIP files
  • Document management services that process user-submitted archives
  • Plugin systems that load ZIP-based extensions
  • Backup/restore endpoints that unpack archives
  • Template engines that process ZIP-based themes

The impact is severe: attackers can overwrite critical system files, plant web shells in web-accessible directories, or execute arbitrary code by placing scripts in executable locations.

Real-world Feathersjs code vulnerable to Zip Slip might look like:

const fs = require('fs');
const AdmZip = require('adm-zip');

class FileUploadService {
  async uploadZip(file) {
    const zip = new AdmZip(file.buffer);
    zip.extractAllTo("./uploads", true); // UNSAFE: no path validation
  }
}

module.exports = FileUploadService;

This code directly extracts to the uploads directory without validating that extracted paths stay within that directory. An attacker could create a ZIP with ../../config/database.json to overwrite configuration files.

Another Feathersjs-specific pattern involves hooks that process uploaded files:

const { hooks } = require('feathers-authentication');

module.exports = {
  before: {
    create: [hooks.authenticate('jwt'), validateZip]
  },
  async upload(context) {
    const { file } = context.data;
    const zip = AdmZip(file.buffer);
    zip.extractAllTo("./user-content", true); // VULNERABLE
    return context;
  }
};

The authentication hook provides a false sense of security—Zip Slip doesn't require authentication to exploit, making this particularly dangerous.

Feathersjs-Specific Detection

Detecting Zip Slip in Feathersjs applications requires examining both code patterns and runtime behavior. Static analysis should focus on these Feathersjs-specific indicators:

  • Services with upload, archive, extract, or zip in their names
  • Hook chains that process file data before validation
  • Configuration files showing multer with storage: diskStorage and no path sanitization
  • Package.json dependencies including adm-zip, yauzl, or node-zip without path validation

Runtime detection with middleBrick specifically identifies Feathersjs Zip Slip vulnerabilities by:

  • Scanning API endpoints that accept multipart/form-data with file parameters
  • Testing for directory traversal in file extraction endpoints
  • Analyzing OpenAPI specs for endpoints with application/zip content types
  • Checking for unsafe use of Node.js file system operations in service methods

Here's how to scan a Feathersjs API with middleBrick:

# Install the CLI
npm install -g middlebrick

# Scan your Feathersjs API
middlebrick scan https://api.yourservice.com

# Or integrate into your Feathersjs project
middlebrick scan http://localhost:3030

middleBrick's Zip Slip detection includes testing with crafted ZIP archives containing paths like:

../../test-payload
../windows/system32/calc.exe
/etc/passwd
/usr/bin/id

The scanner verifies whether these paths escape the intended extraction directory, which would indicate a vulnerable implementation.

For CI/CD integration, add this GitHub Action to your Feathersjs repository:

- name: Run middleBrick Security Scan
  uses: middlebrick/middlebrick-action@v1
  with:
    target-url: http://localhost:3030
    fail-on-severity: high
    output-format: json

This automatically fails your build if Zip Slip vulnerabilities are detected in your Feathersjs API endpoints.

Feathersjs-Specific Remediation

Securely handling ZIP files in Feathersjs requires path validation and safe extraction practices. The most robust approach uses Node.js's built-in path module to validate extraction paths:

const fs = require('fs');
const path = require('path');
const AdmZip = require('adm-zip');

class SecureFileUploadService {
  async uploadZip(file) {
    const zip = new AdmZip(file.buffer);
    const targetDir = path.join(__dirname, '../uploads');
    
    // Validate each entry before extraction
    const zipEntries = zip.getEntries();
    for (const entry of zipEntries) {
      const entryPath = path.join(targetDir, entry.entryName);
      
      // Ensure path is within target directory
      if (!entryPath.startsWith(targetDir + path.sep)) {
        throw new Error(`Invalid path: ${entry.entryName}`);
      }
      
      // Prevent absolute paths
      if (path.isAbsolute(entry.entryName)) {
        throw new Error(`Absolute paths not allowed: ${entry.entryName}`);
      }
      
      // Prevent Windows drive letters
      if (entry.entryName.match(/^[A-Za-z]:/)) {
        throw new Error(`Drive letters not allowed: ${entry.entryName}`);
      }
    }
    
    // Safe to extract
    zip.extractAllTo(targetDir, true);
  }
}

module.exports = SecureFileUploadService;

For Feathersjs hooks, implement validation before extraction:

const path = require('path');
const AdmZip = require('adm-zip');

const validateZipEntries = (zip, targetDir) => {
  const entries = zip.getEntries();
  const resolvedTarget = path.resolve(targetDir);
  
  for (const entry of entries) {
    const entryPath = path.resolve(targetDir, entry.entryName);
    
    if (!entryPath.startsWith(resolvedTarget)) {
      throw new Error(`Zip Slip attempt detected: ${entry.entryName}`);
    }
  }
};

module.exports = {
  before: {
    create: [hooks.authenticate('jwt'), validateZip]
  },
  async upload(context) {
    const { file } = context.data;
    const targetDir = path.join(__dirname, '../user-content');
    
    const zip = new AdmZip(file.buffer);
    validateZipEntries(zip, targetDir);
    zip.extractAllTo(targetDir, true);
    
    return context;
  }
};

Alternative safe extraction using yauzl with streaming:

const fs = require('fs');
const path = require('path');
const yauzl = require('yaulz');

async function safeExtract(zipBuffer, targetDir) {
  return new Promise((resolve, reject) => {
    yauzl.fromBuffer(zipBuffer, { lazyEntries: true }, (err, zipfile) => {
      if (err) return reject(err);
      
      zipfile.readEntry();
      zipfile.on('entry', (entry) => {
        const entryPath = path.join(targetDir, entry.fileName);
        const resolvedTarget = path.resolve(targetDir);
        
        if (!path.resolve(entryPath).startsWith(resolvedTarget)) {
          zipfile.close();
          return reject(new Error('Zip Slip detected'));
        }
        
        if (entry.fileName.endsWith('/')) {
          // Directory
          fs.mkdirSync(entryPath, { recursive: true });
          zipfile.readEntry();
        } else {
          // File
          zipfile.openReadStream(entry, (err, readStream) => {
            if (err) return reject(err);
            
            const dir = path.dirname(entryPath);
            fs.mkdirSync(dir, { recursive: true });
            
            const writeStream = fs.createWriteStream(entryPath);
            readStream.pipe(writeStream);
            writeStream.on('close', () => zipfile.readEntry());
          });
        }
      });
      
      zipfile.once('end', () => resolve());
    });
  });
}

Additional Feathersjs-specific security measures:

  • Use multer with file type validation to reject non-ZIP files
  • Implement size limits on uploaded archives
  • Store extracted files in a dedicated, non-executable directory
  • Set restrictive file permissions on extracted content
  • Add logging for archive processing with entry validation failures

Frequently Asked Questions

How can I test if my Feathersjs API is vulnerable to Zip Slip?
Use middleBrick to scan your API endpoints that accept file uploads. The scanner tests with crafted ZIP archives containing malicious paths like ../../etc/passwd. You can also manually test by creating a ZIP with ../../test.txt and attempting to upload it to your Feathersjs service. If the file appears outside your intended directory, you have a Zip Slip vulnerability.
Does Zip Slip require authentication to exploit?
No, Zip Slip is a pre-authentication vulnerability. An attacker doesn't need valid credentials to exploit it—they only need to upload a malicious ZIP file to an endpoint that processes archives without proper path validation. This makes it particularly dangerous in public APIs or services with open file upload capabilities.