Skip to content

Latest commit

 

History

History
81 lines (59 loc) · 7.46 KB

File metadata and controls

81 lines (59 loc) · 7.46 KB

Mitigation Strategies Analysis for nodejs/readable-stream

Mitigation Strategy: Robust Backpressure Handling

Mitigation Strategy: Robust Backpressure Handling

  • Description:

    1. Analyze pipe() usage: For each pipe() call, verify that the destination writable stream correctly handles backpressure. This means the writable stream should:
      • Return false from its write() method when its internal buffer is full.
      • Emit a 'drain' event when it's ready to receive more data.
    2. Implement manual backpressure (if needed): If pipe() is not used, or if the writable stream doesn't properly handle backpressure, implement manual control:
      • Monitor writable.write(): Check the return value. If false, pause the readable stream.
      • Pause the readable: Call readable.pause().
      • Listen for 'drain': Attach a listener to the writable stream's 'drain' event.
      • Resume the readable: Inside the 'drain' event handler, call readable.resume().
      • Consider readableHighWaterMark and readableLength: If you're not using pipe(), periodically check readable.readableLength against readable.readableHighWaterMark. If readableLength is approaching the high water mark, proactively pause the readable stream before the writable stream's buffer fills up.
    3. Avoid read(0) for backpressure: Do not rely on read(0) as a primary backpressure mechanism.
    4. Use pipeline() where applicable: If possible, refactor to use stream.pipeline() for automatic backpressure and error handling.
  • Threats Mitigated:

    • Denial of Service (DoS) due to Memory Exhaustion (High Severity): A fast producer can overwhelm a slow consumer, leading to excessive memory allocation and application crashes.
    • Application Instability (Medium Severity): Uncontrolled data flow can lead to unpredictable behavior and intermittent failures.
    • Resource Starvation (Medium Severity): Even if the application doesn't crash, excessive memory usage can impact other processes on the system.
  • Impact:

    • DoS due to Memory Exhaustion: Significantly reduced risk. Proper backpressure prevents uncontrolled memory growth.
    • Application Instability: Significantly reduced risk. Controlled data flow leads to more predictable and stable operation.
    • Resource Starvation: Significantly reduced risk. Limits memory usage to acceptable levels.
  • Currently Implemented: [Example: Implemented in the dataProcessingPipeline function in src/pipeline.js using pipeline(). Manual backpressure implemented in the legacy fileUploadHandler in src/upload.js.]

  • Missing Implementation: [Example: Missing in the websocketStreamHandler in src/websocket.js. Currently relies solely on the WebSocket library's internal buffering, which may not be sufficient.]

Mitigation Strategy: Strict highWaterMark Configuration

  • Description:

    1. Determine appropriate highWaterMark values: For each readable stream creation, analyze the expected data size per chunk and available memory.
    2. Set highWaterMark in constructor: Pass the highWaterMark option to the Readable stream constructor (or the constructor of any derived stream class). Set this to a value that balances performance and memory safety. Err on the side of lower values to limit potential memory consumption. Do not rely on the default value.
    3. Document the rationale: Clearly document why a specific highWaterMark value was chosen for each stream.
  • Threats Mitigated:

    • Denial of Service (DoS) due to Memory Exhaustion (High Severity): Limits the maximum amount of data buffered before backpressure is applied, reducing the window of vulnerability.
    • Resource Starvation (Medium Severity): Controls memory usage, preventing excessive allocation.
  • Impact:

    • DoS due to Memory Exhaustion: Significantly reduced risk, especially when combined with proper backpressure handling.
    • Resource Starvation: Significantly reduced risk.
  • Currently Implemented: [Example: highWaterMark is set for all newly created Readable streams in src/utils/streamFactory.js.]

  • Missing Implementation: [Example: highWaterMark is not explicitly set for streams created directly using new Readable() in src/legacy/oldModule.js. These rely on the default value.]

Mitigation Strategy: Comprehensive Stream Error Handling

  • Description:

    1. Attach 'error' listeners: For every stream instance (readable, writable, transform), attach a listener to the 'error' event immediately after the stream is created.
    2. Implement robust error handlers: Within each 'error' event handler:
      • Log the error with sufficient context (stream type, operation, error message, stack trace).
      • Destroy the stream using stream.destroy(err). Always pass the error object to destroy() to ensure proper propagation and to signal the reason for destruction.
      • Clean up any associated resources (e.g., close file handles, database connections, network sockets). This is crucial to prevent leaks.
    3. Prefer pipeline(): Use stream.pipeline() whenever possible. It provides automatic error propagation and stream destruction. The callback function provided to pipeline() will receive any error that occurs.
    4. Handle destroy() errors: Be prepared to handle potential errors that might be emitted during the stream destruction process. This is less common, but possible.
  • Threats Mitigated:

    • Resource Leaks (Medium Severity): Ensures that resources held by the stream (and potentially other related resources) are released even if an error occurs during stream processing.
    • Application Instability (Medium Severity): Prevents unhandled errors from crashing the application or leaving it in an inconsistent state. Proper error handling makes the application more resilient.
    • Denial of Service (DoS) (Low to Medium Severity): While not a primary DoS defense, proper error handling prevents resource leaks that could eventually contribute to a DoS condition if left unaddressed.
  • Impact:

    • Resource Leaks: Significantly reduced risk. Streams and associated resources are cleaned up correctly, preventing memory leaks, file handle exhaustion, etc.
    • Application Instability: Significantly reduced risk. Unhandled errors are caught and managed gracefully, preventing crashes and unexpected behavior.
    • Denial of Service: Indirectly reduces risk by preventing the accumulation of leaked resources.
  • Currently Implemented: [Example: Basic 'error' listeners are present on most streams, but resource cleanup is inconsistent. pipeline() is used in some newer modules.]

  • Missing Implementation: [Example: Consistent resource cleanup is missing in several older modules. Error handling in transform streams is often minimal. pipeline() is not used consistently throughout the codebase. Some stream creations are missing 'error' listeners entirely.]