or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-decompress

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

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/decompress@4.2.x

To install, run

npx @tessl/cli install tessl/npm-decompress@4.2.0

index.mddocs/

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.