Skip to content

Commit c543d68

Browse files
authored
Merge pull request #2349 from trycompai/main
[comp] Production Deploy
2 parents 41559cd + 9786589 commit c543d68

8 files changed

Lines changed: 197 additions & 101 deletions

File tree

‎apps/api/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
"ai": "^5.0.60",
4141
"archiver": "^7.0.1",
4242
"axios": "^1.12.2",
43-
"better-auth": "^1.3.27",
43+
"better-auth": "^1.4.22",
4444
"class-transformer": "^0.5.1",
4545
"class-validator": "^0.14.2",
4646
"dotenv": "^17.2.3",

‎apps/api/src/utils/file-type-validation.spec.ts‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,30 @@ describe('validateFileContent', () => {
4646
const csvBuffer = Buffer.from('name,email\njohn,john@example.com');
4747
expect(() => validateFileContent(csvBuffer, 'text/csv', 'data.csv')).not.toThrow();
4848
});
49+
50+
it('should accept a valid WebP file', () => {
51+
// WebP: RIFF (4 bytes) + size (4 bytes) + WEBP (4 bytes)
52+
const webpBuffer = Buffer.alloc(16);
53+
webpBuffer.write('RIFF', 0);
54+
webpBuffer.writeUInt32LE(8, 4);
55+
webpBuffer.write('WEBP', 8);
56+
expect(() => validateFileContent(webpBuffer, 'image/webp', 'photo.webp')).not.toThrow();
57+
});
58+
59+
it('should reject a WAV file disguised as WebP', () => {
60+
// WAV also starts with RIFF but has WAVE at offset 8, not WEBP
61+
const wavBuffer = Buffer.alloc(16);
62+
wavBuffer.write('RIFF', 0);
63+
wavBuffer.writeUInt32LE(8, 4);
64+
wavBuffer.write('WAVE', 8);
65+
expect(() => validateFileContent(wavBuffer, 'image/webp', 'fake.webp')).toThrow(BadRequestException);
66+
});
67+
68+
it('should reject a RIFF file with script content disguised as WebP', () => {
69+
const malicious = Buffer.alloc(64);
70+
malicious.write('RIFF', 0);
71+
malicious.writeUInt32LE(56, 4);
72+
malicious.write('AVI ', 8); // Not WEBP
73+
expect(() => validateFileContent(malicious, 'image/webp', 'evil.webp')).toThrow(BadRequestException);
74+
});
4975
});

‎apps/api/src/utils/file-type-validation.ts‎

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,24 @@ const MAGIC_BYTES: Record<string, Buffer[]> = {
44
'image/png': [Buffer.from([0x89, 0x50, 0x4e, 0x47])],
55
'image/jpeg': [Buffer.from([0xff, 0xd8, 0xff])],
66
'image/gif': [Buffer.from('GIF87a'), Buffer.from('GIF89a')],
7-
'image/webp': [Buffer.from('RIFF')],
87
'application/pdf': [Buffer.from('%PDF')],
98
'application/zip': [Buffer.from([0x50, 0x4b, 0x03, 0x04])],
109
};
1110

12-
/** MIME types that are verified binary — skip text pattern scanning for these. */
13-
const BINARY_MIME_TYPES = new Set(Object.keys(MAGIC_BYTES));
11+
/**
12+
* RIFF-based formats need extra validation — RIFF is shared by WAV, AVI, WebP, etc.
13+
* WebP files are: RIFF (4 bytes) + file size (4 bytes) + WEBP (4 bytes at offset 8).
14+
*/
15+
const RIFF_HEADER = Buffer.from([0x52, 0x49, 0x46, 0x46]); // RIFF
16+
const WEBP_MARKER = Buffer.from([0x57, 0x45, 0x42, 0x50]); // WEBP
17+
18+
function isValidWebP(fileBuffer: Buffer): boolean {
19+
if (fileBuffer.length < 12) return false;
20+
return (
21+
fileBuffer.subarray(0, 4).equals(RIFF_HEADER) &&
22+
fileBuffer.subarray(8, 12).equals(WEBP_MARKER)
23+
);
24+
}
1425

1526
/**
1627
* Patterns that indicate potentially dangerous HTML/script content.
@@ -34,7 +45,17 @@ export function validateFileContent(
3445
): void {
3546
const lowerMime = declaredMimeType.toLowerCase();
3647

37-
// Check magic bytes for known binary types
48+
// WebP needs special handling — RIFF prefix is shared with WAV, AVI, etc.
49+
if (lowerMime === 'image/webp') {
50+
if (!isValidWebP(fileBuffer)) {
51+
throw new BadRequestException(
52+
'The uploaded file is invalid or corrupted. Please try again with a valid file.',
53+
);
54+
}
55+
return;
56+
}
57+
58+
// Check magic bytes for other known binary types
3859
const expectedSignatures = MAGIC_BYTES[lowerMime];
3960
if (expectedSignatures) {
4061
const matchesSignature = expectedSignatures.some((sig) =>
@@ -46,7 +67,6 @@ export function validateFileContent(
4667
);
4768
}
4869
// Binary file passed magic byte check — skip text pattern scanning
49-
// to avoid false positives from binary data matching text patterns
5070
return;
5171
}
5272

‎apps/app/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484
"ai": "^6.0.116",
8585
"ai-elements": "^1.6.1",
8686
"axios": "^1.9.0",
87-
"better-auth": "^1.3.27",
87+
"better-auth": "^1.4.22",
8888
"botid": "^1.5.5",
8989
"canvas-confetti": "^1.9.3",
9090
"class-variance-authority": "^0.7.1",

‎apps/portal/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"@types/jszip": "^3.4.1",
2121
"@upstash/ratelimit": "^2.0.5",
2222
"archiver": "^7.0.1",
23-
"better-auth": "^1.4.5",
23+
"better-auth": "^1.4.22",
2424
"class-variance-authority": "^0.7.1",
2525
"geist": "^1.3.1",
2626
"jspdf": "^4.2.0",

‎bun.lock‎

Lines changed: 141 additions & 91 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
"@types/cheerio": "^1.0.0",
9393
"@types/react-syntax-highlighter": "^15.5.13",
9494
"@upstash/vector": "^1.2.2",
95-
"better-auth": "1.4.5",
95+
"better-auth": "1.4.22",
9696
"cheerio": "^1.2.0",
9797
"react-syntax-highlighter": "^15.6.6",
9898
"unpdf": "^1.4.0",

‎packages/auth/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"typecheck": "tsc --noEmit"
2424
},
2525
"dependencies": {
26-
"better-auth": "^1.4.5"
26+
"better-auth": "^1.4.22"
2727
},
2828
"peerDependencies": {
2929
"@prisma/client": ">=5.0.0"

0 commit comments

Comments
 (0)