T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/sharepoint.mjs:159
- Finding
- Unbounded Office Archive Expansion Can Cause Resource Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/sharepoint.mjs:159`, `scripts/sharepoint.mjs:242-259` **Vulnerability Type**: Unbounded decompression and document parsing **Risk Level**: Medium ### Vulnerable Code ```js if (meta.size > CFG.maxFileSize) { console.error(`ERROR: File too large (${(meta.size / 1048576).toFixed(1)} MB > ${(CFG.maxFileSize / 1048576).toFixed(1)} MB limit)`); process.exit(1); } // Download content const stream = await client.api(`/drives/${driveId}/root:/${path}:/content`).getStream(); const chunks = []; for await (const chunk of stream) { chunks.push(chunk); } const buf = Buffer.concat(chunks); // Extract text const text = await extractText(buf, meta.name); process.stdout.write(text); ``` The PPTX extraction path subsequently loads and expands the archive without decompression limits: ```js const JSZip = (await import('jszip')).default; if (!JSZip) throw new Error('jszip not available'); const zip = await JSZip.loadAsync(buf); const texts = []; const slideFiles = Object.keys(zip.files) .filter(f => f.match(/^ppt\/slides\/slide\d+\.xml$/)) .sort(); for (const file of slideFiles) { const xml = await zip.files[file].async('string'); const slideText = xml.replace(/<[^>]+>/g, ' ') .replace(/\s+/g, ' ') .trim(); ``` ### Technical Analysis The application enforces `SP_MAX_FILE_SIZE` only against the compressed SharePoint object size. PPTX files are ZIP archives, and their decompressed contents can be substantially larger than the stored file. `JSZip.loadAsync(buf)` processes the archive in the main Node.js process without limits on: - Total decompressed size - Number of ZIP entries - Maximum size of an individual entry - Compression ratio - XML processing size - Parsing duration - Process memory consumption Each selected slide is then expanded completely into a JavaScript string with `async('string')`. A maliciously constructed PPTX can therefore pass the default 50 MB compressed-size chec ...[truncated 1696 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Limit decompressed content** - Reject archives whose declared or observed aggregate uncompressed size exceeds a conservative threshold. - Enforce maximum sizes for individual entries and extracted XML strings. - Reject archives with suspicious compression ratios. 2. **Limit archive structure** - Set a maximum ZIP entry count. - Process only expected PPTX paths. - Reject encrypted, malformed, nested, or otherwise unexpected archives. 3. **Avoid unrestricted string expansion** - Do not call `async('string')` on an entry until its uncompressed size has been validated. - Prefer bounded streaming extraction where supported. - Stop extraction once a maximum output-text size is reached. 4. **Isolate document parsing** - Run Office and PDF parsers in a worker thread or separate subprocess/container. - Apply operating-system or container memory and CPU limits. - Terminate parsing when a strict timeout is exceeded. 5. **Validate actual download size** - Track cumulative downloaded bytes instead of relying exclusively on SharePoint metadata. - Abort the stream immediately when the configured maximum is exceeded. 6. **Fail closed** - Treat limit violations and parser timeouts as security errors. - Return a concise error without retrying the same document automatically. - Record the file identifier and rejection reason in security telemetry without logging document contents or credentials. 7. **Add regression tests** - Test high-compression-ratio PPTX files. - Test oversized XML entries and excessive entry counts. - Confirm that malformed archives cannot exhaust process memory or block execution beyond the configured timeout. ]]>
