CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-decompress

Archive extraction library supporting ZIP, TAR, TAR.GZ, TAR.BZ2, and BZIP2 formats with Promise-based API and security protections

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

Decompress

Decompress is an archive extraction library for Node.js that simplifies extracting files from various archive formats including ZIP, TAR, TAR.GZ, TAR.BZ2, and BZIP2. It provides a Promise-based API with advanced features like file filtering, path mapping, directory stripping, and built-in security protections against path traversal attacks.

Package Information

  • Package Name: decompress
  • Package Type: npm
  • Language: JavaScript
  • Installation: npm install decompress

Core Imports

const decompress = require('decompress');

For ES modules:

import decompress from 'decompress';

Basic Usage

const decompress = require('decompress');

// Extract archive to memory (returns file objects)
decompress('archive.zip').then(files => {
    console.log(`Extracted ${files.length} files`);
    files.forEach(file => {
        console.log(file.path, file.type, file.data.length);
    });
});

// Extract archive to directory
decompress('archive.zip', 'dist').then(files => {
    console.log('Files extracted to dist directory');
});

// Extract with options
decompress('archive.tar.gz', 'output', {
    strip: 1,  // Remove first directory level
    filter: file => file.path.endsWith('.js')  // Only JS files
}).then(files => {
    console.log('JavaScript files extracted');
});

Capabilities

Archive Extraction

Main function for extracting archives with comprehensive format support and security protections.

/**
 * Extract files from various archive formats
 * @param {string|Buffer} input - Path to archive file or Buffer containing archive data
 * @param {string} [output] - Output directory path (optional, omit to extract to memory only)
 * @param {DecompressOptions} [opts] - Configuration options
 * @returns {Promise<FileObject[]>} Promise resolving to array of extracted file objects
 * @throws {TypeError} When input is neither string nor Buffer
 */
function decompress(input, output, opts);

Supported Archive Formats:

  • ZIP files (.zip)
  • TAR archives (.tar)
  • Gzipped TAR archives (.tar.gz, .tgz)
  • Bzip2 TAR archives (.tar.bz2, .tbz2)
  • BZIP2 files (.bz2)

Processing Pipeline:

Files are processed in the following order:

  1. Strip: Leading directory components are removed first
  2. Filter: Files are filtered based on the filter function
  3. Map: Remaining files are transformed using the map function
  4. Write: Files are written to disk (if output directory specified) or returned in memory

Usage Examples:

// Extract from file path to memory
const files = await decompress('data.zip');

// Extract from Buffer to memory
const buffer = fs.readFileSync('data.zip');
const files = await decompress(buffer);

// Extract to specific directory
await decompress('data.tar.gz', './extracted');

// Extract with custom options
await decompress('data.zip', 'output', {
    strip: 2,
    filter: file => !file.path.includes('test'),
    map: file => {
        file.path = 'prefix-' + file.path;
        return file;
    }
});

File Filtering

Filter files during extraction to selectively extract only desired files.

/**
 * Filter function to selectively extract files
 * @param {FileObject} file - File object being processed
 * @returns {boolean} true to extract the file, false to skip
 */
opts.filter = function(file) { return boolean; };

Usage Example:

// Extract only JavaScript files
await decompress('project.zip', 'src', {
    filter: file => file.path.endsWith('.js') || file.path.endsWith('.ts')
});

// Skip hidden files and directories
await decompress('backup.tar.gz', 'restore', {
    filter: file => !file.path.startsWith('.')
});

File Mapping

Transform file objects during extraction to modify paths, metadata, or content.

/**
 * Map function to transform files during extraction
 * @param {FileObject} file - File object being processed
 * @returns {FileObject} Modified file object
 */
opts.map = function(file) { return FileObject; };

Usage Example:

// Add prefix to all extracted file paths
await decompress('archive.zip', 'output', {
    map: file => {
        file.path = 'extracted-' + file.path;
        return file;
    }
});

// Modify file metadata
await decompress('data.tar', 'output', {
    map: file => {
        if (file.type === 'file') {
            file.mode = file.mode | 0o644; // Set read permissions
        }
        return file;
    }
});

Directory Stripping

Remove leading directory components from extracted file paths.

/**
 * Number of leading directory components to strip from file paths
 * @type {number}
 * @default 0
 */
opts.strip = number;

Usage Example:

// Remove first directory level (e.g., "folder/file.txt" becomes "file.txt")
await decompress('nested-archive.tar.gz', 'flat-output', {
    strip: 1
});

// Remove two directory levels
await decompress('deeply/nested/archive.zip', 'output', {
    strip: 2
});

Custom Plugins

Override default decompression plugins with custom handlers. Each plugin is a function that receives input buffer and options, returning a Promise of file objects.

/**
 * Array of decompression plugins to use
 * @type {Function[]}
 * @default [decompressTar(), decompressTarbz2(), decompressTargz(), decompressUnzip()]
 */
opts.plugins = Array<Function>;

/**
 * Plugin function interface
 * @param {Buffer} input - Archive data as Buffer
 * @param {DecompressOptions} opts - Configuration options
 * @returns {Promise<FileObject[]>} Promise resolving to array of file objects
 */
function plugin(input, opts);

Plugin Behavior:

  • All plugins are executed in parallel on the same input
  • Results from all plugins are combined into a single flat array
  • If no plugins are provided or match the format, returns empty array

Usage Example:

const decompressUnzip = require('decompress-unzip');

// Use only ZIP decompression
await decompress('data.zip', 'output', {
    plugins: [decompressUnzip()]
});

// No plugins (useful for getting empty array when no plugins match)
const result = await decompress('unknown.xyz', {
    plugins: []
});
// result will be []

Types

/**
 * Configuration options for decompress function
 */
interface DecompressOptions {
    /** Filter function to selectively extract files */
    filter?: (file: FileObject) => boolean;
    /** Map function to transform files during extraction */
    map?: (file: FileObject) => FileObject;
    /** Array of decompression plugins */
    plugins?: Function[];
    /** Number of leading directory components to strip */
    strip?: number;
}

/**
 * File object representing an extracted file
 */
interface FileObject {
    /** File contents as Buffer */
    data: Buffer;
    /** File permissions/mode as number (affected by process umask when written) */
    mode: number;
    /** File modification time as Date object */
    mtime: Date;
    /** Relative file path within the archive */
    path: string;
    /** File type: 'file', 'directory', 'link', or 'symlink' */
    type: 'file' | 'directory' | 'link' | 'symlink';
    /** Target path for links and symlinks (only present when type is 'link' or 'symlink') */
    linkname?: string;
}

File Type Handling

Different file types are handled as follows when extracting to disk:

  • Regular Files: Written using fs.writeFile() with mode applied (affected by umask)
  • Directories: Created using makeDir() with timestamps set via fs.utimes()
  • Hard Links: Created using fs.link() to link to existing files
  • Symbolic Links: Created using fs.symlink() (or fs.link() on Windows for compatibility)

Platform Differences:

  • On Windows, symbolic links are created as hard links for compatibility
  • File modes are processed through the system umask before being applied

Error Handling

The decompress function throws errors with specific messages:

  • Invalid Input: TypeError: "Input file required" when input is neither string nor Buffer
  • File Not Found: System ENOENT error when specified archive file doesn't exist
  • Path Traversal: "Refusing to create a directory outside the output path." for directory escapes
  • Symlink Security: "Refusing to write into a symlink" when attempting to write through symlinks
  • Directory Escape: "Refusing to write outside output directory: <path>" for file path escapes
  • Permission Errors: System errors when unable to create directories or write files
  • Corrupted Archive: Plugin-specific errors when archive format is invalid

Error Handling Example:

try {
    const files = await decompress('archive.zip', 'output');
    console.log('Extraction successful');
} catch (error) {
    if (error instanceof TypeError && error.message === 'Input file required') {
        console.error('Invalid input provided');
    } else if (error.message.includes('Refusing')) {
        console.error('Security violation detected:', error.message);
    } else if (error.code === 'ENOENT') {
        console.error('Archive file not found');
    } else {
        console.error('Extraction failed:', error.message);
    }
}

Security Features

Decompress includes built-in security protections against malicious archives:

  • Path Traversal Protection: Prevents extraction of files outside the target directory using real path validation
  • Symlink Validation: Blocks symlinks that would escape the output directory and refuses to write through existing symlinks
  • Real Path Resolution: Uses fs.realpath() to validate all paths are within allowed boundaries before file operations
  • Directory Escape Prevention: Validates parent directories are within output path before creation
  • Zip Bomb Protection: Files with path "." are filtered out to prevent current directory overwrite

Security Implementation Details:

  • All file and directory paths are resolved to their real paths before validation
  • Parent directory validation occurs recursively up the directory tree
  • Both directory creation and file writing are protected by boundary checks
  • The system uses graceful-fs for safer concurrent file operations

These protections help prevent malicious archives from compromising the system through directory traversal attacks, symlink-based escapes, and other archive-based security vulnerabilities.

Install with Tessl CLI

npx tessl i tessl/npm-decompress
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/decompress@4.2.x
Publish Source
CLI
Badge
tessl/npm-decompress badge