T09 · Insecure Skill Coding Practices
- Location
scripts/sharepoint.mjs:159- Finding
Unbounded Office Archive Expansion Can Cause Resource Exhaustion
- Content
View full analysis
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
View remediation
