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:
co-authored by
Claude Opus 4.6
parent
36e9eb36d8
commit
07dd376b3e
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user