Skip to content

Commit 6594698

Browse files
committed
chore(cleanup): reuse zip-guard EOCD parsing in the file sniffer
1 parent 2a805e1 commit 6594698

2 files changed

Lines changed: 20 additions & 54 deletions

File tree

‎apps/sim/lib/file-parsers/sniff.ts‎

Lines changed: 12 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,16 @@ import { FileParserError } from '@/lib/file-parsers/errors'
22
import { isEncryptedOoxmlContainer } from '@/lib/file-parsers/ooxml-encryption'
33
import type { FileParseOptions } from '@/lib/file-parsers/types'
44
import { decodeTextBuffer, detectBomlessUtf16 } from '@/lib/file-parsers/utils'
5-
import { isZipShaped } from '@/lib/file-parsers/zip-guard'
5+
import {
6+
CENTRAL_DIRECTORY_HEADER_MIN_SIZE,
7+
CENTRAL_DIRECTORY_HEADER_SIGNATURE,
8+
COMPRESSION_METHOD_STORED,
9+
EOCD_MIN_SIZE,
10+
findEocdOffset,
11+
isZipShaped,
12+
LOCAL_FILE_HEADER_MIN_SIZE,
13+
locateCentralDirectory,
14+
} from '@/lib/file-parsers/zip-guard'
615

716
/**
817
* What the bytes of a buffer look like, independent of the caller-supplied
@@ -34,18 +43,6 @@ const OLE2_SIGNATURE = Buffer.from([0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0x
3443
/** An RTF file is a group opening with the `rtf` control word; nothing may precede it. */
3544
const RTF_SIGNATURE = Buffer.from('{\\rtf', 'latin1')
3645

37-
const EOCD_SIGNATURE = 0x06054b50
38-
const EOCD_MIN_SIZE = 22
39-
const MAX_EOCD_COMMENT_SIZE = 0xffff
40-
const ZIP64_EOCD_LOCATOR_SIGNATURE = 0x07064b50
41-
const ZIP64_EOCD_LOCATOR_SIZE = 20
42-
const ZIP64_EOCD_SIGNATURE = 0x06064b50
43-
const CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x02014b50
44-
const CENTRAL_DIRECTORY_HEADER_MIN_SIZE = 46
45-
const LOCAL_FILE_HEADER_MIN_SIZE = 30
46-
const COMPRESSION_METHOD_STORED = 0
47-
const UINT16_SENTINEL = 0xffff
48-
const UINT32_SENTINEL = 0xffffffff
4946
/** Enough to reach the first `word/`, `xl/` or `ppt/` part in any real package. */
5047
const MAX_INSPECTED_ENTRIES = 256
5148
const MAX_MIMETYPE_BYTES = 128
@@ -63,37 +60,6 @@ interface ZipEntry {
6360
localHeaderOffset: number
6461
}
6562

66-
/** Same EOCD anchoring as the zip guard: only a record whose comment ends the buffer counts. */
67-
function findEocdOffset(buffer: Buffer): number {
68-
const minStart = Math.max(0, buffer.length - EOCD_MIN_SIZE - MAX_EOCD_COMMENT_SIZE)
69-
for (let offset = buffer.length - EOCD_MIN_SIZE; offset >= minStart; offset--) {
70-
if (buffer.readUInt32LE(offset) !== EOCD_SIGNATURE) continue
71-
const commentLength = buffer.readUInt16LE(offset + 20)
72-
if (offset + EOCD_MIN_SIZE + commentLength === buffer.length) return offset
73-
}
74-
return -1
75-
}
76-
77-
function locateCentralDirectory(buffer: Buffer, eocdOffset: number): number | null {
78-
const entryCount = buffer.readUInt16LE(eocdOffset + 10)
79-
const directoryOffset = buffer.readUInt32LE(eocdOffset + 16)
80-
if (entryCount !== UINT16_SENTINEL && directoryOffset !== UINT32_SENTINEL) {
81-
return directoryOffset
82-
}
83-
84-
const locatorOffset = eocdOffset - ZIP64_EOCD_LOCATOR_SIZE
85-
if (locatorOffset < 0 || buffer.readUInt32LE(locatorOffset) !== ZIP64_EOCD_LOCATOR_SIGNATURE) {
86-
return null
87-
}
88-
const zip64Eocd = buffer.readBigUInt64LE(locatorOffset + 8)
89-
if (zip64Eocd > BigInt(buffer.length - 56)) return null
90-
const zip64EocdOffset = Number(zip64Eocd)
91-
if (buffer.readUInt32LE(zip64EocdOffset) !== ZIP64_EOCD_SIGNATURE) return null
92-
const zip64DirectoryOffset = buffer.readBigUInt64LE(zip64EocdOffset + 48)
93-
if (zip64DirectoryOffset > BigInt(buffer.length)) return null
94-
return Number(zip64DirectoryOffset)
95-
}
96-
9763
/**
9864
* Reads central-directory entry names without decompressing anything. Returns
9965
* `null` for a buffer whose directory cannot be located. Bounded to the first
@@ -104,8 +70,8 @@ function readZipEntries(buffer: Buffer): ZipEntry[] | null {
10470
if (buffer.length < EOCD_MIN_SIZE) return null
10571
const eocdOffset = findEocdOffset(buffer)
10672
if (eocdOffset < 0) return null
107-
const directoryOffset = locateCentralDirectory(buffer, eocdOffset)
108-
if (directoryOffset === null) return null
73+
const directoryOffset = locateCentralDirectory(buffer, eocdOffset)?.offset
74+
if (directoryOffset === undefined) return null
10975

11076
const entries: ZipEntry[] = []
11177
let cursor = directoryOffset

‎apps/sim/lib/file-parsers/zip-guard.ts‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,18 @@ const LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50
2929
const EOCD_SIGNATURE = 0x06054b50
3030
const ZIP64_EOCD_LOCATOR_SIGNATURE = 0x07064b50
3131
const ZIP64_EOCD_SIGNATURE = 0x06064b50
32-
const CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x02014b50
32+
export const CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x02014b50
3333
const ZIP64_EXTRA_FIELD_ID = 0x0001
3434

35-
const EOCD_MIN_SIZE = 22
35+
export const EOCD_MIN_SIZE = 22
3636
const ZIP64_EOCD_LOCATOR_SIZE = 20
37-
const CENTRAL_DIRECTORY_HEADER_MIN_SIZE = 46
38-
const LOCAL_FILE_HEADER_MIN_SIZE = 30
37+
export const CENTRAL_DIRECTORY_HEADER_MIN_SIZE = 46
38+
export const LOCAL_FILE_HEADER_MIN_SIZE = 30
3939
const MAX_EOCD_COMMENT_SIZE = 0xffff
4040
const UINT32_SENTINEL = 0xffffffff
4141
const UINT16_SENTINEL = 0xffff
4242

43-
const COMPRESSION_METHOD_STORED = 0
43+
export const COMPRESSION_METHOD_STORED = 0
4444
const COMPRESSION_METHOD_DEFLATE = 8
4545

4646
/** General-purpose bit 3: sizes live in a trailing data descriptor, not the local header. */
@@ -99,7 +99,7 @@ export function isZipShaped(buffer: Buffer): boolean {
9999
* at the buffer tail, so a decoy EOCD signature planted in the comment region
100100
* cannot redirect the guard to a smaller, attacker-chosen central directory.
101101
*/
102-
function findEocdOffset(buffer: Buffer): number {
102+
export function findEocdOffset(buffer: Buffer): number {
103103
const minStart = Math.max(0, buffer.length - EOCD_MIN_SIZE - MAX_EOCD_COMMENT_SIZE)
104104
for (let offset = buffer.length - EOCD_MIN_SIZE; offset >= minStart; offset--) {
105105
if (buffer.readUInt32LE(offset) !== EOCD_SIGNATURE) {
@@ -113,7 +113,7 @@ function findEocdOffset(buffer: Buffer): number {
113113
return -1
114114
}
115115

116-
interface CentralDirectoryLocation {
116+
export interface CentralDirectoryLocation {
117117
offset: number
118118
entryCount: number
119119
}
@@ -122,7 +122,7 @@ interface CentralDirectoryLocation {
122122
* Resolve the central directory offset and entry count, following the ZIP64
123123
* end-of-central-directory chain when the 32-bit fields are saturated.
124124
*/
125-
function locateCentralDirectory(
125+
export function locateCentralDirectory(
126126
buffer: Buffer,
127127
eocdOffset: number
128128
): CentralDirectoryLocation | null {

0 commit comments

Comments
 (0)