Profiles·Public

sharp

semver>=0.30.0 <1.0.0postconditions5functions5last verified2026-06-24coverage score100%

Postconditions: what we check

  • toFile · tofile-rejects-on-error
    error
    Whenfile system error, invalid image data, or processing failure
    ThrowsPromise rejection with Error (ENOENT, EACCES, ENOMEM, or processing errors)
    Required handlingCaller MUST use try-catch or .catch() to handle Promise rejections from toFile(). File system errors (missing directory, permissions), invalid image data, and memory issues will reject the Promise and crash the application if unhandled. Use pattern: try { await sharp(input).toFile('output.jpg'); } catch (error) { /* handle */ }
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1]
  • toBuffer · tobuffer-rejects-on-error
    error
    Wheninvalid image data or processing failure
    ThrowsPromise rejection with Error
    Required handlingCaller MUST use try-catch or .catch() to handle Promise rejections from toBuffer(). Invalid or corrupted image data will reject the Promise and crash if unhandled. Use pattern: try { const buffer = await sharp(input).toBuffer(); } catch (error) { /* handle */ }
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[2]
  • metadata · metadata-rejects-on-corrupt-or-unsupported-input
    error
    WhenInput image has a corrupt header, is not a recognized image format, or the input file is missing from the filesystem. Also rejects if the image exceeds the pixel limit (default 268,402,689 pixels) when limitInputPixels is set.
    ThrowsPromise rejection with Error whose message begins with one of: "Input file is missing: <path>", "Input file has corrupt header: <libvips error>", "Input file contains unsupported image format", "Input buffer has corrupt header: <libvips error>", "Input buffer contains unsupported image format", "Input image exceeds pixel limit". All originate from libvips VError converted to a native JS Error via is.nativeError() in lib/input.js.
    Required handlingCaller MUST wrap await sharp(input).metadata() in try-catch. Used in upload validation pipelines to check if a file is a genuine image — if the call throws, it must be caught or it silently crashes the upload handler and returns a 500 to the client. Correct pattern: try { const meta = await sharp(inputBuffer).metadata(); // Use meta.width, meta.height, meta.format } catch (error) { // Input is corrupt, unsupported, or file is missing throw new Error(`Invalid image: ${error.message}`); } Note: metadata() reads only the header — a successful metadata() call does NOT guarantee the full image is valid. Corrupt pixel data surfaces only when toBuffer() or toFile() decodes the full image.
    costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[3][4][5]
  • stats · stats-rejects-on-corrupt-or-unsupported-input
    error
    WhenInput image cannot be fully decoded: corrupt file, unsupported format, missing input file, or pixel limit exceeded. Unlike metadata(), stats() decodes all pixel data so corrupt pixel data (not just header corruption) will also cause rejection.
    ThrowsPromise rejection with Error whose message originates from libvips VError: "Input file is missing: <path>", "Input file has corrupt header: <libvips error>", "Input file contains unsupported image format", "Input buffer has corrupt header: <libvips error>", "Input buffer contains unsupported image format", or any libvips processing error from full pixel decode.
    Required handlingCaller MUST wrap await sharp(input).stats() in try-catch. stats() is often used for image quality analysis (is it blurry? colorful?). When called without error handling on unvalidated user uploads, a corrupt image will crash the analysis pipeline and leave jobs stuck. Correct pattern: try { const { entropy, sharpness, dominant } = await sharp(inputBuffer).stats(); // Use entropy to detect blank images, sharpness for blur detection } catch (error) { console.error('Image stats failed:', error.message); throw error; } Critical gotcha: statistics are derived from the ORIGINAL input image. If you want stats on a resized/cropped region, you MUST first do: const part = await sharp(input).extract(region).toBuffer(); const s = await sharp(part).stats(); // Stats on the cropped region Calling .extract(region).stats() without intermediate toBuffer() reads stats from the original unmodified image — a silent data correctness bug.
    costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[6][7][4]
  • toUint8Array · touint8array-rejects-on-error
    error
    WhenInvalid image data, corrupt input buffer, unsupported format, missing input file, or pixel-decode failure during the libvips pipeline. toUint8Array() shares the same _pipeline() rejection path as toBuffer() and toFile() — any error surfaced by libvips during decode/encode causes a Promise rejection.
    ThrowsPromise rejection with Error whose message originates from libvips VError via is.nativeError(): typical messages include "Input file is missing:", "Input buffer has corrupt header:", "Input buffer contains unsupported image format", or any libvips processing error from full pixel decode.
    Required handlingCaller MUST wrap await sharp(input).toUint8Array() in try-catch or attach .catch(). The most common use case (zero-copy transfer to a worker via postMessage) is a hot path where unhandled rejections crash the worker process and orphan in-flight jobs. Correct pattern: try { const { data, info } = await sharp(inputBuffer) .resize(800, 600) .png() .toUint8Array(); worker.postMessage({ data, info }, [data.buffer]); } catch (error) { // Corrupt input or unsupported format throw new Error(`Image encode failed: ${error.message}`); } Critical gotcha: the transferable ArrayBuffer means that if you forget to `await` and the Promise rejects asynchronously, the worker thread receives a stale or partially-populated buffer — and the rejection lands on the global unhandledRejection handler instead of the original call site. Always `await` (or `.then`/`.catch`) the returned Promise.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[8][9]

Sources

Every postcondition cites at least one of these. Grouped by source type; numbered to match the footnotes above.

Official documentation
Source code

Research notes

Curator notes from SOURCES.md captured when the profile was written so you can verify the reasoning, not just the rules.

Sources: sharp

Package: sharp Category: image-processing Last Updated: 2026-02-27 Status: ✅ COMPLETE


Official Documentation


Behavioral Requirements

Error Pattern: Promise-based async operations that reject on errors

Key Methods:

  • toFile() - Rejects on file system errors or invalid image data
  • toBuffer() - Rejects on processing errors or invalid input

Required Handling: Always use try-catch or .catch() for Promise rejections


Contract Rationale

Sharp methods return Promises that reject when:

  • File system operations fail (ENOENT, EACCES, ENOMEM)
  • Image data is invalid or corrupted
  • Processing operations fail (unsupported format, memory limits)

Without proper error handling, these rejections cause application crashes.


Security Considerations

Vulnerability History

CVE-2023-4863 (HIGH SEVERITY - CVSS 8.8)

  • Affected Versions: sharp < 0.32.6
  • Component: libwebp dependency
  • Vulnerability: Heap buffer overflow
  • Attack Vector: Malicious WebP images
  • Impact: Arbitrary code execution, denial of service
  • Mitigation: Upgrade to sharp >=0.32.6 + proper error handling
  • Source: https://security.snyk.io/vuln/SNYK-JS-SHARP-2848109

CVE-2023-40032 (MEDIUM SEVERITY)

  • Affected Versions: libvips 8.12.0 - 8.14.3
  • Component: SVG loader (svgload)
  • Vulnerability: NULL pointer dereference
  • Attack Vector: Crafted SVG files
  • Impact: Application crash, denial of service
  • Mitigation: Upgrade libvips >=8.14.4 + error handling prevents crash
  • Source: https://www.cvedetails.com/cve/CVE-2023-40032/

CVE-2025-29769 (MEDIUM-HIGH SEVERITY)

  • Affected Versions: libvips < 8.16.1
  • Component: HEIF save operation
  • Vulnerability: Heap buffer overflow
  • Attack Vector: 4-channel TIFF images during HEIC conversion
  • Impact: Process crash, potential code execution
  • Mitigation: Upgrade libvips >=8.16.1 + handle conversion errors
  • Source: https://nvd.nist.gov/vuln/detail/cve-2025-29769

CVE-2025-59933 (MEDIUM SEVERITY)

  • Affected Versions: libvips <=8.17.1 (when compiled with PDF support)
  • Component: PDF parser (via poppler)
  • Vulnerability: Buffer read overflow
  • Attack Vector: Crafted PDF with missing height definition
  • Impact: Process crash, potential information disclosure
  • Mitigation: Upgrade libvips >=8.17.2
  • Note: Most sharp installations don't include PDF support

Error Handling as Security Defense

Proper error handling provides critical security benefits:

  1. Prevents Crashes - Try-catch blocks prevent malicious images from crashing the application
  2. Enables Logging - Catch blocks allow logging of potential attack attempts
  3. Isolates Failures - Error handling in batch operations prevents one bad file from crashing entire process
  4. Graceful Degradation - Application continues running instead of complete failure

Recommended Minimum Version

sharp >= 0.32.6 (fixes CVE-2023-4863 - high severity WebP vulnerability)

Best Practices for Secure Image Processing

  1. Always wrap user-provided image inputs in try-catch

    try {
      await sharp(userUploadedBuffer).toBuffer();
    } catch (error) {
      logger.warn('Invalid or malicious image detected');
      throw new Error('Invalid image file');
    }
    
  2. Validate image dimensions before processing (prevent memory exhaustion)

    const metadata = await sharp(buffer).metadata();
    if (metadata.width > 4096 || metadata.height > 4096) {
      throw new Error('Image dimensions exceed limits');
    }
    
  3. Implement rate limiting for image processing endpoints

  4. Log processing failures (may indicate attack attempts)

    } catch (error) {
      logger.warn('Image processing failed', {
        error: error.message,
        userId,
        timestamp: Date.now()
      });
    }
    
  5. Isolate failures in batch operations

    for (const file of files) {
      try {
        await sharp(file).resize(800, 600).toFile(`out/${file}`);
      } catch (error) {
        // Log but continue processing other files
        logger.error(`Failed to process ${file}:`, error);
      }
    }
    

Example: Secure Upload Handler

async function processUserUpload(buffer: Buffer) {
  try {
    // Step 1: Validate metadata (fail fast on invalid images)
    const metadata = await sharp(buffer).metadata();

    // Step 2: Check size limits (prevent memory exhaustion)
    if (metadata.width > 4096 || metadata.height > 4096) {
      throw new Error('Image dimensions exceed limits');
    }

    // Step 3: Process with error handling
    const processed = await sharp(buffer)
      .resize(800, 600, { fit: 'inside' })
      .jpeg({ quality: 85 })
      .toBuffer();

    return processed;
  } catch (error) {
    // Step 4: Log potential attack
    logger.warn('Malicious or invalid image detected', {
      error: error.message,
      bufferSize: buffer.length
    });

    throw new Error('Invalid image file');
  }
}

Real-World Attack Scenarios

Scenario 1: API Endpoint Without Error Handling

// ❌ VULNERABLE - One malicious image crashes the entire API
app.post('/upload', async (req, res) => {
  const buffer = await sharp(req.file.buffer)
    .resize(800, 600)
    .toBuffer();  // CVE-2023-4863 can crash here
  res.send(buffer);
});

// ✅ PROTECTED - Error handling prevents crash
app.post('/upload', async (req, res) => {
  try {
    const buffer = await sharp(req.file.buffer)
      .resize(800, 600)
      .toBuffer();
    res.send(buffer);
  } catch (error) {
    logger.warn('Upload processing failed', { error });
    res.status(400).json({ error: 'Invalid image file' });
  }
});

Scenario 2: Batch Processing

// ❌ VULNERABLE - First malicious image crashes entire batch
async function processBatch(files: string[]) {
  for (const file of files) {
    await sharp(file).resize(800, 600).toFile(`out/${file}`);
  }
}

// ✅ PROTECTED - Continues processing after errors
async function processBatch(files: string[]) {
  const results = [];
  for (const file of files) {
    try {
      await sharp(file).resize(800, 600).toFile(`out/${file}`);
      results.push({ file, status: 'success' });
    } catch (error) {
      logger.error(`Failed to process ${file}:`, error);
      results.push({ file, status: 'failed', error: error.message });
    }
  }
  return results;
}

Created: 2026-02-26 Enhanced: 2026-02-27 Research: dev-notes/package-onboarding/sharp/

Need a different package?
Request a profile