fix: address PR review findings for screenshot paste capability

- Add randomBytes to historyFile name to fix CodeQL predictable temp file warning
- Resolve tmp_dir in Python to fix macOS symlink validation (is_relative_to)
- Convert writeFileSync to async writeFile to prevent main thread blocking
- Move SAFE_EXT_MAP to module scope to avoid repeated allocation
- Only write manifest file when manifest.length > 0 (skip empty manifests)

Co-Authored-By: Claude Opus 4.6 <[email protected]>
This commit is contained in:
AndyMik90
2026-02-17 15:32:37 +01:00
co-authored by Claude Opus 4.6
parent 36e9eb36d8
commit 07dd376b3e
2 changed files with 25 additions and 21 deletions
+1 -1
View File
@@ -129,7 +129,7 @@ def load_images_from_manifest(manifest_path: str) -> list[dict]:
Returns a list of dicts with 'media_type' and 'data' (base64-encoded) fields.
"""
images = []
tmp_dir = Path(tempfile.gettempdir())
tmp_dir = Path(tempfile.gettempdir()).resolve()
try:
with open(manifest_path, encoding="utf-8") as f:
@@ -1,5 +1,6 @@
import { spawn, ChildProcess } from 'child_process';
import { existsSync, writeFileSync, unlinkSync } from 'fs';
import { existsSync, unlinkSync } from 'fs';
import { writeFile } from 'fs/promises';
import { randomBytes } from 'crypto';
import path from 'path';
import os from 'os';
@@ -16,6 +17,16 @@ import { MODEL_ID_MAP } from '../../shared/constants';
import { InsightsConfig } from './config';
import { detectRateLimit, createSDKRateLimitInfo } from '../rate-limit-detector';
// Safe extension map for image MIME types — prevents path traversal via crafted mimeType
// SVG excluded: contains active script content and is unsupported by Claude Vision API
const SAFE_EXT_MAP: Record<string, string> = {
'image/png': 'png',
'image/jpeg': 'jpg',
'image/jpg': 'jpg',
'image/gif': 'gif',
'image/webp': 'webp'
};
/**
* Message processor result
*/
@@ -93,12 +104,12 @@ export class InsightsExecutor extends EventEmitter {
// Write conversation history to temp file to avoid Windows command-line length limit
const historyFile = path.join(
os.tmpdir(),
`insights-history-${projectId}-${Date.now()}.json`
`insights-history-${projectId}-${Date.now()}-${randomBytes(8).toString('hex')}.json`
);
let historyFileCreated = false;
try {
writeFileSync(historyFile, JSON.stringify(conversationHistory), 'utf-8');
await writeFile(historyFile, JSON.stringify(conversationHistory), 'utf-8');
historyFileCreated = true;
} catch (err) {
console.error('[Insights] Failed to write history file:', err);
@@ -109,16 +120,6 @@ export class InsightsExecutor extends EventEmitter {
const imagesTempFiles: string[] = [];
let imagesManifestFile: string | undefined;
// Safe extension map for image MIME types — prevents path traversal via crafted mimeType
// SVG excluded: contains active script content and is unsupported by Claude Vision API
const SAFE_EXT_MAP: Record<string, string> = {
'image/png': 'png',
'image/jpeg': 'jpg',
'image/jpg': 'jpg',
'image/gif': 'gif',
'image/webp': 'webp'
};
if (images && images.length > 0) {
try {
const manifest: Array<{ path: string; mimeType: string }> = [];
@@ -139,17 +140,20 @@ export class InsightsExecutor extends EventEmitter {
os.tmpdir(),
`insights-image-${projectId}-${timestamp}-${i}-${randomBytes(8).toString('hex')}.${ext}`
);
writeFileSync(imagePath, Buffer.from(image.data, 'base64'));
await writeFile(imagePath, Buffer.from(image.data, 'base64'));
imagesTempFiles.push(imagePath);
manifest.push({ path: imagePath, mimeType: image.mimeType });
}
imagesManifestFile = path.join(
os.tmpdir(),
`insights-images-manifest-${projectId}-${timestamp}-${randomBytes(8).toString('hex')}.json`
);
writeFileSync(imagesManifestFile, JSON.stringify(manifest), 'utf-8');
imagesTempFiles.push(imagesManifestFile);
// Only write manifest file if we actually wrote any images
if (manifest.length > 0) {
imagesManifestFile = path.join(
os.tmpdir(),
`insights-images-manifest-${projectId}-${timestamp}-${randomBytes(8).toString('hex')}.json`
);
await writeFile(imagesManifestFile, JSON.stringify(manifest), 'utf-8');
imagesTempFiles.push(imagesManifestFile);
}
} catch (err) {
// Clean up any already-written image files
for (const tmpFile of imagesTempFiles) {