Initial commit: Memind H5 portal with MindSpace, Plaza, and agent jobs.
Track application source and tests; exclude local env, user workspaces, and runtime data via .gitignore. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
const SCRIPT_PATTERNS = [
|
||||
/<script\b/i,
|
||||
/javascript:/i,
|
||||
/\bon\w+\s*=/i,
|
||||
/<iframe\b/i,
|
||||
/<object\b/i,
|
||||
/<embed\b/i,
|
||||
];
|
||||
|
||||
const MAX_ZIP_COMPRESSION_RATIO = 100;
|
||||
const MAX_ZIP_LOCAL_HEADERS = 512;
|
||||
|
||||
function isZipContainer(mimeType) {
|
||||
return (
|
||||
mimeType.includes('openxmlformats') ||
|
||||
mimeType.includes('msword') ||
|
||||
mimeType.includes('ms-excel') ||
|
||||
mimeType.includes('ms-powerpoint')
|
||||
);
|
||||
}
|
||||
|
||||
function scanTextContent(buffer) {
|
||||
const sample = buffer.subarray(0, Math.min(buffer.length, 256 * 1024)).toString('utf8');
|
||||
for (const pattern of SCRIPT_PATTERNS) {
|
||||
if (pattern.test(sample)) {
|
||||
return {
|
||||
scanStatus: 'blocked',
|
||||
riskLevel: 'high',
|
||||
findings: ['text_active_content'],
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function scanZipStructure(buffer) {
|
||||
if (buffer.subarray(0, 2).toString('ascii') !== 'PK') {
|
||||
return {
|
||||
scanStatus: 'blocked',
|
||||
riskLevel: 'medium',
|
||||
findings: ['zip_signature_mismatch'],
|
||||
};
|
||||
}
|
||||
|
||||
let offset = 0;
|
||||
let headers = 0;
|
||||
while (offset + 30 <= buffer.length && headers < MAX_ZIP_LOCAL_HEADERS) {
|
||||
if (buffer.subarray(offset, offset + 2).toString('ascii') !== 'PK') break;
|
||||
const compressionMethod = buffer.readUInt16LE(offset + 8);
|
||||
const compressedSize = buffer.readUInt32LE(offset + 18);
|
||||
const uncompressedSize = buffer.readUInt32LE(offset + 22);
|
||||
const nameLength = buffer.readUInt16LE(offset + 26);
|
||||
const extraLength = buffer.readUInt16LE(offset + 28);
|
||||
const headerSize = 30 + nameLength + extraLength;
|
||||
if (headerSize <= 0 || offset + headerSize > buffer.length) break;
|
||||
|
||||
if (compressionMethod !== 0 && compressedSize > 0 && uncompressedSize / compressedSize > MAX_ZIP_COMPRESSION_RATIO) {
|
||||
return {
|
||||
scanStatus: 'blocked',
|
||||
riskLevel: 'critical',
|
||||
findings: ['zip_bomb_ratio'],
|
||||
};
|
||||
}
|
||||
|
||||
headers += 1;
|
||||
offset += headerSize;
|
||||
if (compressedSize > 0) {
|
||||
offset += compressedSize;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function runBasicFileScan(buffer, { filename, mimeType }) {
|
||||
if (!Buffer.isBuffer(buffer) || buffer.length === 0) {
|
||||
return {
|
||||
scanStatus: 'blocked',
|
||||
riskLevel: 'high',
|
||||
findings: ['empty_file'],
|
||||
};
|
||||
}
|
||||
|
||||
if (mimeType.startsWith('text/') || mimeType === 'application/pdf') {
|
||||
const textFinding = scanTextContent(buffer);
|
||||
if (textFinding) return textFinding;
|
||||
}
|
||||
|
||||
if (mimeType === 'application/pdf' && buffer.subarray(0, 5).toString('ascii') !== '%PDF-') {
|
||||
return {
|
||||
scanStatus: 'blocked',
|
||||
riskLevel: 'medium',
|
||||
findings: ['pdf_signature_mismatch'],
|
||||
};
|
||||
}
|
||||
|
||||
if (isZipContainer(mimeType)) {
|
||||
const zipFinding = scanZipStructure(buffer);
|
||||
if (zipFinding) return zipFinding;
|
||||
}
|
||||
|
||||
return {
|
||||
scanStatus: 'passed',
|
||||
riskLevel: 'none',
|
||||
findings: [],
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user