fix(security): prevent memory exhaustion in inline image decoding#22325
fix(security): prevent memory exhaustion in inline image decoding#22325BradGroux merged 1 commit intoopenclaw:mainfrom
Conversation
Additional Comments (1)
Prompt To Fix With AIThis is a comment left during a code review.
Path: extensions/msteams/src/attachments/download.ts
Line: 221-223
Comment:
This check is redundant since `decodeDataImageWithLimits()` already enforces `maxInlineBytes` and won't return a candidate if the estimated size exceeds the limit (shared.ts:213-214). The `inline.data.byteLength` here would never exceed `params.maxBytes`.
How can I resolve this? If you propose a fix, please make it concise. |
|
Can you check the latest commit @greptileai |
387be1a to
d9a6e39
Compare
|
Adding explicit test results from local test: |
|
This pull request has been automatically marked as stale due to inactivity. |
|
This PR is still active, and I believe it's being added in a future release of multiple MSTeam fixes. |
|
This pull request has been automatically marked as stale due to inactivity. |
|
This PR is still active, and I believe it's being added in a future release of multiple MSTeam fixes. |
Summary
Describe the problem and fix in 2–5 bullets:
Problem: MS Teams inbound attachment parsing is vulnerable to memory exhaustion (DoS). Inline
data:image/...;base64,...content from HTML attachments is decoded directly into a Buffer before size limits are enforced. An attacker can send a large base64 payload that triggers massive allocations duringBuffer.from()decode, causing high memory pressure or process instability.Why it matters: This is a denial-of-service vector. An attacker can send specially crafted Teams messages with oversized inline images to exhaust process memory, crash the service, or degrade performance for legitimate users.
What changed:
estimateBase64DecodedBytes()andisLikelyBase64Payload()helpers to validate and estimate decoded size before allocationdecodeDataImage()→decodeDataImageWithLimits()to enforce per-image byte limits beforeBuffer.from()InlineImageLimitOptionstype for per-image and cumulative byte budgetsextractInlineImageCandidates()to accept and enforce limits, tracking total estimated bytesdownloadMSTeamsAttachments()to pass byte constraints through the call pathWhat did NOT change: User-facing API behavior remains identical; limits enforcement is internal to security-sensitive parsing paths.
Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
User-visible / Behavior Changes
None. The fix is purely internal DoS mitigation. Legitimate attachments (within configured byte limits) are processed identically.
Security Impact (required)
Explanation: This is a defensive change that hardens parsing against memory exhaustion attacks. It adds pre-allocation size validation but does not expand attack surface. Untrusted inline base64 payloads are now constrained at parse time rather than after unbounded memory allocation.
Repro + Verification
Environment
maxBytesparameter todownloadMSTeamsAttachments()(existing)Steps
Before fix (vulnerable):
data:image/png;base64,+ 1GB base64-encoded payloaddecodeDataImage()→Buffer.from(payload, "base64")After fix (hardened):
decodeDataImageWithLimits()withmaxInlineBytes: params.maxBytes(e.g., 10MB)estimateBase64DecodedBytes()returns estimate ~750MBif (estimatedBytes > opts.maxInlineBytes) return nullBuffer.from()allocationExpected
Actual
✓ Verified in test scenarios; large payloads rejected before allocation
Evidence
Buffer.from()in all pathsdecodeDataImageWithLimits()return structure checkeddownloadMSTeamsAttachments()→extractInlineImageCandidates(limits)→decodeDataImageWithLimits()Human Verification (required)
What you personally verified (not just CI), and how:
Verified scenarios:
maxInlineTotalByteslimit enforced across multiple inline imagesisLikelyBase64Payload()Edge cases checked:
What you did NOT verify:
Compatibility / Migration
The
limitsparameter toextractInlineImageCandidates()is optional; callers not passing it receive default behavior (no limits). Existing call sites must be updated to pass limits for protection (e.g.,downloadMSTeamsAttachments()already does).Failure Recovery (if this breaks)
limitsargument fromextractInlineImageCandidates()calls; revert toshared.tsanddownload.tsto prior versions without size validationextensions/msteams/src/attachments/shared.ts,extensions/msteams/src/attachments/download.tsmaxBytes)decodeDataImageWithLimits()return structure (ensure callers handle bothcandidateandestimatedBytes)Risks and Mitigations
Risk: Misconfigured
maxByteslimit too small, rejecting legitimate inline imagesparams.maxBytespassed todownloadMSTeamsAttachments()(already set for other attachments); audit limit value in production configRisk: Estimation math error causes false rejections or false accepts
estimateBase64DecodedBytes()conservatively calculatesfloor((len * 3) / 4) - padding; verified against standard base64 decode formulaRisk: Attacker crafts non-base64 payload to bypass
isLikelyBase64Payload()^[A-Za-z0-9+/=\r\n]+$is strict; invalid base64 still rejected inBuffer.from()catch block (defense-in-depth)Risk: Cumulative limit tracking error across multiple inline images
totalEstimatedInlineBytes; boundary check uses estimated bytes before allocation, breaking loop at or before limitGreptile Summary
Adds memory exhaustion protection to MS Teams inline image parsing by validating base64 payload sizes before allocation. The fix introduces
estimateBase64DecodedBytes()to calculate decoded size upfront anddecodeDataImageWithLimits()to enforce per-image and cumulative byte limits before callingBuffer.from(). The cumulative limit tracking correctly uses labeledbreak outerLoopto stop processing when total bytes exceed configured limits. The base64 size estimation formulafloor((len * 3) / 4) - paddingis mathematically correct and matches actual decoded sizes. Defense-in-depth is maintained with the redundantbyteLengthcheck after allocation.Confidence Score: 5/5
Last reviewed commit: 8c32c61