upload-safety 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -4
- package/dist/heif-metadata.d.ts +14 -0
- package/dist/heif-metadata.d.ts.map +1 -0
- package/dist/heif-metadata.js +216 -0
- package/dist/heif-metadata.js.map +1 -0
- package/dist/photo-metadata.d.ts +5 -4
- package/dist/photo-metadata.d.ts.map +1 -1
- package/dist/photo-metadata.js +84 -4
- package/dist/photo-metadata.js.map +1 -1
- package/dist/text-content.d.ts +7 -0
- package/dist/text-content.d.ts.map +1 -0
- package/dist/text-content.js +83 -0
- package/dist/text-content.js.map +1 -0
- package/dist/upload-allowlist.d.ts +2 -2
- package/dist/upload-allowlist.d.ts.map +1 -1
- package/dist/upload-allowlist.js +27 -1
- package/dist/upload-allowlist.js.map +1 -1
- package/package.json +7 -3
- package/src/heif-metadata.ts +232 -0
- package/src/photo-metadata.ts +88 -4
- package/src/text-content.ts +85 -0
- package/src/upload-allowlist.ts +33 -4
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](package.json)
|
|
8
8
|
[](src/index.ts)
|
|
9
9
|
|
|
10
|
-
Server-side upload checks for Node that read the file, not just its name. An upload passes only when its extension, its declared MIME type and its bytes agree, and when it carries nothing that runs on its own: no macros, embedded objects, DDE fields or
|
|
10
|
+
Server-side upload checks for Node that read the file, not just its name. An upload passes only when its extension, its declared MIME type and its bytes agree, and when it carries nothing that runs on its own: no macros, embedded objects, DDE fields, PDF scripts or CSV formulas. Photos, including iPhone HEIC, lose their GPS position and other metadata on the way through.
|
|
11
11
|
|
|
12
12
|
For apps that take CVs, invoices, contracts and photos from strangers and hand them to colleagues who open them in Office or Acrobat.
|
|
13
13
|
|
|
@@ -30,9 +30,11 @@ if ( ! result.ok ) {
|
|
|
30
30
|
| `form.pdf` with JavaScript, a launch action or an attachment, also in compressed object streams | refused, `macros` |
|
|
31
31
|
| `budget.xls` with a very hidden Excel 4.0 macro sheet | refused, `macros` |
|
|
32
32
|
| `notes.odt` with a Basic macro or an event listener | refused, `macros` |
|
|
33
|
+
| `export.csv` with `=HYPERLINK(...)` or `=cmd\|' /C calc'!A0` in a cell | refused, `macros` |
|
|
33
34
|
| `contract.docx` saved with a password | refused, `encrypted` |
|
|
34
35
|
| `data.xlsx` that is a zip bomb | refused, `content-mismatch` |
|
|
35
36
|
| `holiday.jpg` with GPS coordinates | accepted, EXIF, XMP, IPTC and comments removed, orientation kept |
|
|
37
|
+
| `IMG_0042.heic` from an iPhone | accepted, EXIF and XMP overwritten in place, pixels unchanged |
|
|
36
38
|
| `../../CON.pdf`, a plain PDF | accepted, stored as `_CON.pdf` |
|
|
37
39
|
|
|
38
40
|
Every check fails closed: a part it cannot read is a refusal, not a pass.
|
|
@@ -42,7 +44,7 @@ Every check fails closed: a part it cannot read is a refusal, not a pass.
|
|
|
42
44
|
- **Zero runtime dependencies.** Only `node:zlib` and `node:crypto`. Nothing to audit but this package.
|
|
43
45
|
- **Typed reasons.** Each refusal is one of seven string literals, so a `Record<UploadRejection, string>` of messages is checked by the compiler.
|
|
44
46
|
- **Fast enough to run inline.** Synchronous; 647 real documents up to 20 MB each took at most 65 ms on a laptop.
|
|
45
|
-
- **Tested against crafted files.**
|
|
47
|
+
- **Tested against crafted files.** 235 tests build malformed ZIPs, compound files and PDFs byte by byte, next to real Office and PDF files.
|
|
46
48
|
|
|
47
49
|
## Install
|
|
48
50
|
|
|
@@ -132,9 +134,11 @@ app.post( '/upload', upload.single( 'file' ), ( req, res ) => {
|
|
|
132
134
|
| Office (OOXML) | `docx` `xlsx` `pptx` | Required parts; VBA and ActiveX parts; macro content types and relationships; embedded and linked OLE objects; altChunk imports; subdocuments, frames and remote templates; DDE fields in Word and DDE links in Excel; encryption |
|
|
133
135
|
| Office (legacy) | `doc` `xls` `ppt` | OLE directory; VBA storages; embedded OLE objects; Excel 4.0 macro sheets; DDE and OLE links in Excel; PowerPoint VBA and object records; encryption flags |
|
|
134
136
|
| OpenDocument | `odt` `ods` `odp` | The `mimetype` entry; Basic and script modules; embedded OLE objects; event listeners; links to macros; encrypted manifests |
|
|
135
|
-
| Photos | `jpg` `jpeg` `png` | Structure; EXIF (GPS included), XMP, IPTC and
|
|
137
|
+
| Photos | `jpg` `jpeg` `png` `webp` | Structure; EXIF (GPS included), XMP, IPTC, comments and any other metadata segment or chunk are removed, the orientation is kept |
|
|
138
|
+
| HEIF photos | `heic` `heif` `avif` | Brands; EXIF and XMP items are overwritten in place, since dropping them would shift the offsets the file is built on. The orientation lives in the image properties and stays. Image sequences, such as animated AVIF, are refused |
|
|
139
|
+
| Text | `csv` `txt` | No binary data (UTF-8, UTF-16 with a byte order mark, or a Windows code page); in CSV no cell that starts like a formula (`=`, `+`, `-`, `@`) and calls a function or DDE. Numbers such as `-5` or `+49 (30) 1234` pass |
|
|
136
140
|
|
|
137
|
-
ZIP and OLE reading is capped against zip bombs and malformed sector chains. A PDF that asks for a password is refused. One that only restricts permissions opens without a password and passes, unless its object streams are encrypted, since then they cannot be checked.
|
|
141
|
+
ZIP and OLE reading is capped against zip bombs and malformed sector chains. Every format is in the default list; pass `allowedExtensions` to accept fewer. A PDF that asks for a password is refused. One that only restricts permissions opens without a password and passes, unless its object streams are encrypted, since then they cannot be checked.
|
|
138
142
|
|
|
139
143
|
## API
|
|
140
144
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Removes EXIF and XMP from a HEIF still image (HEIC, AVIF) without moving a byte. The item
|
|
3
|
+
* locations are byte offsets that dropping data would shift, so the items are overwritten in
|
|
4
|
+
* place instead: EXIF with an empty TIFF block, XMP with spaces. The orientation lives in the
|
|
5
|
+
* `irot` and `imir` properties, which stay. Image sequences (`moov`) are refused.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* The HEIF image with its EXIF and XMP items overwritten, or null when its boxes do not read,
|
|
9
|
+
* an item's data cannot be located, or it is an image sequence.
|
|
10
|
+
*/
|
|
11
|
+
export declare function stripHeif(content: Buffer): Buffer | null;
|
|
12
|
+
/** The major and compatible brands of a file's leading `ftyp` box, empty when there is none. */
|
|
13
|
+
export declare function readFtypBrands(content: Buffer): string[];
|
|
14
|
+
//# sourceMappingURL=heif-metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heif-metadata.d.ts","sourceRoot":"","sources":["../src/heif-metadata.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA2KH;;;GAGG;AACH,wBAAgB,SAAS,CAAE,OAAO,EAAE,MAAM,GAAI,MAAM,GAAG,IAAI,CAqC1D;AAED,gGAAgG;AAChG,wBAAgB,cAAc,CAAE,OAAO,EAAE,MAAM,GAAI,MAAM,EAAE,CAW1D"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Removes EXIF and XMP from a HEIF still image (HEIC, AVIF) without moving a byte. The item
|
|
3
|
+
* locations are byte offsets that dropping data would shift, so the items are overwritten in
|
|
4
|
+
* place instead: EXIF with an empty TIFF block, XMP with spaces. The orientation lives in the
|
|
5
|
+
* `irot` and `imir` properties, which stay. Image sequences (`moov`) are refused.
|
|
6
|
+
*/
|
|
7
|
+
/** Bounds far above real files that keep a crafted one cheap. */
|
|
8
|
+
const MAX_BOXES = 10_000;
|
|
9
|
+
const MAX_ITEMS = 100_000;
|
|
10
|
+
const XMP_CONTENT_TYPE = 'application/rdf+xml';
|
|
11
|
+
/** An EXIF item: the offset of the TIFF header (0), then a big-endian TIFF block with an empty IFD0. */
|
|
12
|
+
const EMPTY_EXIF = Buffer.from('00000000' + '4d4d002a00000008' + '0000' + '00000000', 'hex');
|
|
13
|
+
const SPACE = 0x20;
|
|
14
|
+
/** The boxes in `[start, end)`, or null when one runs past it or there are too many. */
|
|
15
|
+
function readBoxes(bytes, start, end) {
|
|
16
|
+
const boxes = [];
|
|
17
|
+
for (let offset = start; offset < end;) {
|
|
18
|
+
if (offset + 8 > end || boxes.length === MAX_BOXES) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
let size = bytes.readUInt32BE(offset);
|
|
22
|
+
let header = 8;
|
|
23
|
+
if (size === 1) {
|
|
24
|
+
const large = offset + 16 <= end ? bytes.readBigUInt64BE(offset + 8) : undefined;
|
|
25
|
+
if (large === undefined || large > BigInt(end - offset)) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
size = Number(large);
|
|
29
|
+
header = 16;
|
|
30
|
+
}
|
|
31
|
+
else if (size === 0) {
|
|
32
|
+
// Runs to the end of the file.
|
|
33
|
+
size = end - offset;
|
|
34
|
+
}
|
|
35
|
+
const type = bytes.toString('latin1', offset + 4, offset + 8);
|
|
36
|
+
header += type === 'uuid' ? 16 : 0;
|
|
37
|
+
if (size < header || offset + size > end) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
boxes.push({ type, start: offset + header, end: offset + size });
|
|
41
|
+
offset += size;
|
|
42
|
+
}
|
|
43
|
+
return boxes;
|
|
44
|
+
}
|
|
45
|
+
/** Reads the fields of a box's content in order; any read past its end throws. */
|
|
46
|
+
class FieldReader {
|
|
47
|
+
position;
|
|
48
|
+
bytes;
|
|
49
|
+
end;
|
|
50
|
+
constructor(bytes, box) {
|
|
51
|
+
this.bytes = bytes;
|
|
52
|
+
this.position = box.start;
|
|
53
|
+
this.end = box.end;
|
|
54
|
+
}
|
|
55
|
+
/** An unsigned big-endian integer of 0, 1, 2, 4 or 8 bytes. */
|
|
56
|
+
uint(size) {
|
|
57
|
+
if (this.position + size > this.end) {
|
|
58
|
+
throw new RangeError('Box field past its end');
|
|
59
|
+
}
|
|
60
|
+
const value = size === 8 ? Number(this.bytes.readBigUInt64BE(this.position)) : size === 0 ? 0 : this.bytes.readUIntBE(this.position, size);
|
|
61
|
+
this.position += size;
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
text(length) {
|
|
65
|
+
if (this.position + length > this.end) {
|
|
66
|
+
throw new RangeError('Box field past its end');
|
|
67
|
+
}
|
|
68
|
+
this.position += length;
|
|
69
|
+
return this.bytes.toString('latin1', this.position - length, this.position);
|
|
70
|
+
}
|
|
71
|
+
/** A NUL-terminated string, or the rest of the box when the NUL is missing. */
|
|
72
|
+
string() {
|
|
73
|
+
const nul = this.bytes.indexOf(0, this.position);
|
|
74
|
+
const stop = nul < 0 || nul >= this.end ? this.end : nul;
|
|
75
|
+
const value = this.bytes.toString('utf8', this.position, stop);
|
|
76
|
+
this.position = Math.min(this.end, stop + 1);
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** The IDs of the EXIF and XMP items an `iinf` box lists; throws a RangeError when it does not read. */
|
|
81
|
+
function readMetadataItems(bytes, iinf) {
|
|
82
|
+
const reader = new FieldReader(bytes, iinf);
|
|
83
|
+
const version = reader.uint(1);
|
|
84
|
+
reader.uint(3);
|
|
85
|
+
reader.uint(version === 0 ? 2 : 4);
|
|
86
|
+
const exif = new Set();
|
|
87
|
+
const xmp = new Set();
|
|
88
|
+
const entries = readBoxes(bytes, reader.position, iinf.end);
|
|
89
|
+
if (!entries) {
|
|
90
|
+
throw new RangeError('Item entries do not read');
|
|
91
|
+
}
|
|
92
|
+
for (const infe of entries) {
|
|
93
|
+
if (infe.type !== 'infe') {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const entry = new FieldReader(bytes, infe);
|
|
97
|
+
const entryVersion = entry.uint(1);
|
|
98
|
+
entry.uint(3);
|
|
99
|
+
const id = entry.uint(entryVersion === 3 ? 4 : 2);
|
|
100
|
+
entry.uint(2);
|
|
101
|
+
// Versions 0 and 1 describe a MIME item without naming its type.
|
|
102
|
+
const type = entryVersion >= 2 ? entry.text(4) : 'mime';
|
|
103
|
+
entry.string();
|
|
104
|
+
if (type === 'Exif') {
|
|
105
|
+
exif.add(id);
|
|
106
|
+
}
|
|
107
|
+
else if (type === 'mime' && entry.string().toLowerCase() === XMP_CONTENT_TYPE) {
|
|
108
|
+
xmp.add(id);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return { exif, xmp };
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Where the data of each item lies, as file offsets read from an `iloc` box, `[ -1, -1 ]` for an
|
|
115
|
+
* extent that cannot be located; throws a RangeError when the box does not read.
|
|
116
|
+
*/
|
|
117
|
+
function readItemLocations(bytes, iloc, idat) {
|
|
118
|
+
const reader = new FieldReader(bytes, iloc);
|
|
119
|
+
const version = reader.uint(1);
|
|
120
|
+
reader.uint(3);
|
|
121
|
+
const sizes = reader.uint(2);
|
|
122
|
+
const offsetSize = sizes >> 12;
|
|
123
|
+
const lengthSize = (sizes >> 8) & 0xf;
|
|
124
|
+
const baseOffsetSize = (sizes >> 4) & 0xf;
|
|
125
|
+
const indexSize = version === 0 ? 0 : sizes & 0xf;
|
|
126
|
+
const itemCount = reader.uint(version < 2 ? 2 : 4);
|
|
127
|
+
if (itemCount > MAX_ITEMS) {
|
|
128
|
+
throw new RangeError('Too many items');
|
|
129
|
+
}
|
|
130
|
+
const locations = new Map();
|
|
131
|
+
for (let item = 0; item < itemCount; item++) {
|
|
132
|
+
const id = reader.uint(version < 2 ? 2 : 4);
|
|
133
|
+
const method = version === 0 ? 0 : reader.uint(2) & 0xf;
|
|
134
|
+
const dataReference = reader.uint(2);
|
|
135
|
+
const baseOffset = reader.uint(baseOffsetSize);
|
|
136
|
+
const extentCount = reader.uint(2);
|
|
137
|
+
const extents = [];
|
|
138
|
+
for (let extent = 0; extent < extentCount; extent++) {
|
|
139
|
+
reader.uint(indexSize);
|
|
140
|
+
const offset = reader.uint(offsetSize);
|
|
141
|
+
const length = reader.uint(lengthSize);
|
|
142
|
+
// Method 0 points into the file, 1 into the `idat` box; 2 (other items) and other files are not read.
|
|
143
|
+
const origin = method === 0 && dataReference === 0 ? 0 : method === 1 ? idat?.start : undefined;
|
|
144
|
+
const start = origin === undefined ? -1 : origin + baseOffset + offset;
|
|
145
|
+
const limit = method === 1 ? idat?.end ?? -1 : bytes.length;
|
|
146
|
+
extents.push(start < 0 || length === 0 || start + length > limit ? [-1, -1] : [start, start + length]);
|
|
147
|
+
}
|
|
148
|
+
locations.set(id, [...(locations.get(id) ?? []), ...extents]);
|
|
149
|
+
}
|
|
150
|
+
return locations;
|
|
151
|
+
}
|
|
152
|
+
/** Writes `data`, padded with `fill`, over the extents in order. */
|
|
153
|
+
function overwrite(bytes, extents, data, fill) {
|
|
154
|
+
let written = 0;
|
|
155
|
+
for (const [start, end] of extents) {
|
|
156
|
+
bytes.fill(fill, start, end);
|
|
157
|
+
written += data.copy(bytes, start, Math.min(written, data.length), Math.min(data.length, written + end - start));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* The HEIF image with its EXIF and XMP items overwritten, or null when its boxes do not read,
|
|
162
|
+
* an item's data cannot be located, or it is an image sequence.
|
|
163
|
+
*/
|
|
164
|
+
export function stripHeif(content) {
|
|
165
|
+
const boxes = readBoxes(content, 0, content.length);
|
|
166
|
+
if (!boxes || boxes[0]?.type !== 'ftyp' || boxes.some((box) => box.type === 'moov')) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
const metas = boxes.filter((box) => box.type === 'meta');
|
|
170
|
+
// `meta` is a full box: version and flags come before its children.
|
|
171
|
+
const children = metas.length === 1 ? readBoxes(content, metas[0].start + 4, metas[0].end) : null;
|
|
172
|
+
const iinf = children?.find((box) => box.type === 'iinf');
|
|
173
|
+
const iloc = children?.find((box) => box.type === 'iloc');
|
|
174
|
+
if (!iinf || !iloc) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
const stripped = Buffer.from(content);
|
|
178
|
+
try {
|
|
179
|
+
const { exif, xmp } = readMetadataItems(content, iinf);
|
|
180
|
+
const locations = readItemLocations(content, iloc, children.find((box) => box.type === 'idat'));
|
|
181
|
+
for (const [ids, data, fill] of [[exif, EMPTY_EXIF, 0], [xmp, Buffer.alloc(0), SPACE]]) {
|
|
182
|
+
for (const id of ids) {
|
|
183
|
+
const extents = locations.get(id) ?? [];
|
|
184
|
+
if (extents.some(([start]) => start < 0)) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
overwrite(stripped, extents, data, fill);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
if (error instanceof RangeError) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
throw error;
|
|
196
|
+
}
|
|
197
|
+
// Top-level `uuid` boxes hold vendor data, such as XMP in some writers.
|
|
198
|
+
for (const box of boxes.filter((box) => box.type === 'uuid')) {
|
|
199
|
+
stripped.fill(0, box.start, box.end);
|
|
200
|
+
}
|
|
201
|
+
return stripped;
|
|
202
|
+
}
|
|
203
|
+
/** The major and compatible brands of a file's leading `ftyp` box, empty when there is none. */
|
|
204
|
+
export function readFtypBrands(content) {
|
|
205
|
+
const size = content.length >= 16 ? content.readUInt32BE(0) : 0;
|
|
206
|
+
if (size < 16 || size > content.length || content.toString('latin1', 4, 8) !== 'ftyp') {
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
// The minor version sits between the major brand and the compatible ones.
|
|
210
|
+
const brands = [content.toString('latin1', 8, 12)];
|
|
211
|
+
for (let offset = 16; offset + 4 <= size; offset += 4) {
|
|
212
|
+
brands.push(content.toString('latin1', offset, offset + 4));
|
|
213
|
+
}
|
|
214
|
+
return brands;
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=heif-metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heif-metadata.js","sourceRoot":"","sources":["../src/heif-metadata.ts"],"names":[],"mappings":"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"}
|
package/dist/photo-metadata.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Removes what a photo tells about where, when and with what it was taken (EXIF with GPS,
|
|
3
|
-
* XMP, IPTC, comments) without decoding it: JPEG segments and PNG chunks are dropped
|
|
4
|
-
* so the image data stays byte for byte. Only the orientation is written back, since
|
|
5
|
-
* it a portrait photo from a phone opens on its side.
|
|
3
|
+
* XMP, IPTC, comments) without decoding it: JPEG segments and PNG and WebP chunks are dropped
|
|
4
|
+
* whole, so the image data stays byte for byte. Only the orientation is written back, since
|
|
5
|
+
* without it a portrait photo from a phone opens on its side. HEIF images are handled in
|
|
6
|
+
* `heif-metadata.ts`.
|
|
6
7
|
*/
|
|
7
8
|
import type { AllowedExtension } from './upload-allowlist.js';
|
|
8
9
|
/**
|
|
9
|
-
* The bytes to store for an upload: a
|
|
10
|
+
* The bytes to store for an upload: a photo without its metadata, any other file
|
|
10
11
|
* unchanged; null for a photo whose structure does not read.
|
|
11
12
|
*/
|
|
12
13
|
export declare function stripPhotoMetadata(extension: AllowedExtension, bytes: Uint8Array): Uint8Array | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"photo-metadata.d.ts","sourceRoot":"","sources":["../src/photo-metadata.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"photo-metadata.d.ts","sourceRoot":"","sources":["../src/photo-metadata.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAmS9D;;;GAGG;AACH,wBAAgB,kBAAkB,CAAE,SAAS,EAAE,gBAAgB,EAAE,KAAK,EAAE,UAAU,GAAI,UAAU,GAAG,IAAI,CAiBtG"}
|
package/dist/photo-metadata.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Removes what a photo tells about where, when and with what it was taken (EXIF with GPS,
|
|
3
|
-
* XMP, IPTC, comments) without decoding it: JPEG segments and PNG chunks are dropped
|
|
4
|
-
* so the image data stays byte for byte. Only the orientation is written back, since
|
|
5
|
-
* it a portrait photo from a phone opens on its side.
|
|
3
|
+
* XMP, IPTC, comments) without decoding it: JPEG segments and PNG and WebP chunks are dropped
|
|
4
|
+
* whole, so the image data stays byte for byte. Only the orientation is written back, since
|
|
5
|
+
* without it a portrait photo from a phone opens on its side. HEIF images are handled in
|
|
6
|
+
* `heif-metadata.ts`.
|
|
6
7
|
*/
|
|
7
8
|
import { crc32 } from 'node:zlib';
|
|
9
|
+
import { stripHeif } from './heif-metadata.js';
|
|
8
10
|
const ORIENTATION_TAG = 0x0112;
|
|
9
11
|
const SHORT_TYPE = 3;
|
|
10
12
|
const UPRIGHT = 1;
|
|
@@ -32,6 +34,14 @@ const PNG_IMAGE_CHUNKS = new Set([
|
|
|
32
34
|
'IHDR', 'PLTE', 'IDAT', 'IEND', 'tRNS', 'cHRM', 'gAMA', 'iCCP', 'sBIT', 'sRGB', 'cICP', 'mDCV', 'cLLI',
|
|
33
35
|
'bKGD', 'hIST', 'pHYs', 'sPLT', 'acTL', 'fcTL', 'fdAT',
|
|
34
36
|
]);
|
|
37
|
+
const WEBP_HEADER_SIZE = 12;
|
|
38
|
+
const RIFF_CHUNK_HEADER_SIZE = 8;
|
|
39
|
+
// The chunks that draw the image, as for PNG; EXIF, XMP and any other chunk may carry metadata.
|
|
40
|
+
const WEBP_IMAGE_CHUNKS = new Set(['VP8X', 'VP8 ', 'VP8L', 'ALPH', 'ANIM', 'ANMF', 'ICCP']);
|
|
41
|
+
const WEBP_BITSTREAM_CHUNKS = new Set(['VP8 ', 'VP8L', 'ANMF']);
|
|
42
|
+
/** VP8X flags of the metadata chunks, in the first byte of its payload. */
|
|
43
|
+
const VP8X_EXIF = 0x08;
|
|
44
|
+
const VP8X_XMP = 0x04;
|
|
35
45
|
/** The orientation (1 to 8) in IFD0 of a TIFF block, or undefined when there is none or it does not read. */
|
|
36
46
|
function readOrientation(tiff) {
|
|
37
47
|
const order = tiff.toString('latin1', 0, 2);
|
|
@@ -208,8 +218,72 @@ function stripPng(bytes) {
|
|
|
208
218
|
}
|
|
209
219
|
return null;
|
|
210
220
|
}
|
|
221
|
+
function riffChunk(type, payload) {
|
|
222
|
+
const chunk = Buffer.alloc(RIFF_CHUNK_HEADER_SIZE + payload.length + (payload.length & 1));
|
|
223
|
+
chunk.write(type, 0, 'latin1');
|
|
224
|
+
chunk.writeUInt32LE(payload.length, 4);
|
|
225
|
+
payload.copy(chunk, RIFF_CHUNK_HEADER_SIZE);
|
|
226
|
+
return chunk;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* The WebP without metadata chunks, or null when its chunks do not read or none draws the image.
|
|
230
|
+
* Bytes after the RIFF size are cut off. The orientation needs the extended format (VP8X) to be
|
|
231
|
+
* written back, as a simple WebP cannot hold EXIF at all.
|
|
232
|
+
*/
|
|
233
|
+
function stripWebp(bytes) {
|
|
234
|
+
if (bytes.length < WEBP_HEADER_SIZE || bytes.toString('latin1', 0, 4) !== 'RIFF' || bytes.toString('latin1', 8, 12) !== 'WEBP') {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
const end = RIFF_CHUNK_HEADER_SIZE + bytes.readUInt32LE(4);
|
|
238
|
+
if (end > bytes.length) {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
const chunks = [];
|
|
242
|
+
let extended;
|
|
243
|
+
let orientation;
|
|
244
|
+
let drawn = false;
|
|
245
|
+
for (let offset = WEBP_HEADER_SIZE; offset < end;) {
|
|
246
|
+
const dataStart = offset + RIFF_CHUNK_HEADER_SIZE;
|
|
247
|
+
const dataEnd = dataStart + (dataStart <= end ? bytes.readUInt32LE(offset + 4) : 0);
|
|
248
|
+
if (dataStart > end || dataEnd > end) {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
const type = bytes.toString('latin1', offset, offset + 4);
|
|
252
|
+
const payload = bytes.subarray(dataStart, dataEnd);
|
|
253
|
+
if (type === 'EXIF') {
|
|
254
|
+
// The format wants the TIFF block alone; some writers put the JPEG `Exif` header first.
|
|
255
|
+
orientation ??= readOrientation(payload.subarray(0, EXIF_HEADER.length).equals(EXIF_HEADER) ? payload.subarray(EXIF_HEADER.length) : payload);
|
|
256
|
+
}
|
|
257
|
+
if (WEBP_IMAGE_CHUNKS.has(type)) {
|
|
258
|
+
if (type === 'VP8X') {
|
|
259
|
+
extended ??= { index: chunks.length, flags: Buffer.from(payload) };
|
|
260
|
+
}
|
|
261
|
+
chunks.push(riffChunk(type, payload));
|
|
262
|
+
drawn ||= WEBP_BITSTREAM_CHUNKS.has(type);
|
|
263
|
+
}
|
|
264
|
+
offset = dataEnd + ((dataEnd - dataStart) & 1);
|
|
265
|
+
}
|
|
266
|
+
if (!drawn) {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
if (extended?.flags.length) {
|
|
270
|
+
const { index, flags } = extended;
|
|
271
|
+
const writesOrientation = orientation !== undefined && orientation !== UPRIGHT;
|
|
272
|
+
flags[0] = (flags[0] & ~(VP8X_EXIF | VP8X_XMP)) | (writesOrientation ? VP8X_EXIF : 0);
|
|
273
|
+
chunks[index] = riffChunk('VP8X', flags);
|
|
274
|
+
if (writesOrientation) {
|
|
275
|
+
// EXIF goes after the image data.
|
|
276
|
+
chunks.push(riffChunk('EXIF', orientationTiff(orientation)));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const header = Buffer.alloc(WEBP_HEADER_SIZE);
|
|
280
|
+
header.write('RIFF', 0, 'latin1');
|
|
281
|
+
header.writeUInt32LE(4 + chunks.reduce((sum, chunk) => sum + chunk.length, 0), 4);
|
|
282
|
+
header.write('WEBP', 8, 'latin1');
|
|
283
|
+
return Buffer.concat([header, ...chunks]);
|
|
284
|
+
}
|
|
211
285
|
/**
|
|
212
|
-
* The bytes to store for an upload: a
|
|
286
|
+
* The bytes to store for an upload: a photo without its metadata, any other file
|
|
213
287
|
* unchanged; null for a photo whose structure does not read.
|
|
214
288
|
*/
|
|
215
289
|
export function stripPhotoMetadata(extension, bytes) {
|
|
@@ -220,6 +294,12 @@ export function stripPhotoMetadata(extension, bytes) {
|
|
|
220
294
|
return stripJpeg(content);
|
|
221
295
|
case 'png':
|
|
222
296
|
return stripPng(content);
|
|
297
|
+
case 'webp':
|
|
298
|
+
return stripWebp(content);
|
|
299
|
+
case 'heic':
|
|
300
|
+
case 'heif':
|
|
301
|
+
case 'avif':
|
|
302
|
+
return stripHeif(content);
|
|
223
303
|
default:
|
|
224
304
|
return bytes;
|
|
225
305
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"photo-metadata.js","sourceRoot":"","sources":["../src/photo-metadata.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"photo-metadata.js","sourceRoot":"","sources":["../src/photo-metadata.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks that a text upload is text, and that a CSV file holds no formula that runs something
|
|
3
|
+
* when Excel or another spreadsheet opens it (CSV injection).
|
|
4
|
+
*/
|
|
5
|
+
/** Whether a text upload is text, and for CSV free of formulas that call a function or DDE. */
|
|
6
|
+
export declare function inspectText(bytes: Uint8Array, csv: boolean): 'valid' | 'macros' | 'mismatch';
|
|
7
|
+
//# sourceMappingURL=text-content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-content.d.ts","sourceRoot":"","sources":["../src/text-content.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAmEH,+FAA+F;AAC/F,wBAAgB,WAAW,CAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,GAAI,OAAO,GAAG,QAAQ,GAAG,UAAU,CAa9F"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks that a text upload is text, and that a CSV file holds no formula that runs something
|
|
3
|
+
* when Excel or another spreadsheet opens it (CSV injection).
|
|
4
|
+
*/
|
|
5
|
+
const UTF16LE_BOM = [0xff, 0xfe];
|
|
6
|
+
const UTF16BE_BOM = [0xfe, 0xff];
|
|
7
|
+
const UTF8_BOM = '';
|
|
8
|
+
// Text has tabs, line breaks and form feeds, but no other control characters; NUL marks UTF-16 without a byte order mark or binary data.
|
|
9
|
+
const CONTROL_CHARACTER = /[\0-\x08\x0b\x0e-\x1f\x7f]/;
|
|
10
|
+
// Excel and others evaluate a cell that starts with one of these (after spaces, tabs or quotes).
|
|
11
|
+
const FORMULA_START = /^[\s"]*[=+\-@]/;
|
|
12
|
+
// A formula runs something through a function call (HYPERLINK, WEBSERVICE) or a DDE source (`cmd|'/c calc'!A1`).
|
|
13
|
+
// Excel allows no space between a function and its parenthesis, so numbers such as `-5`, `+49 (30) 1234` or
|
|
14
|
+
// `+66 2 550 6555; Counter (Gate A)` and plain arithmetic stay allowed.
|
|
15
|
+
const FUNCTION_CALL_OR_DDE = /[A-Za-z_][\w.]*\(|\|/;
|
|
16
|
+
// Splitting on every common delimiter at once only finds more cell starts than a spreadsheet would.
|
|
17
|
+
const CELL_DELIMITERS = /[,;\t\r\n]/g;
|
|
18
|
+
function startsWith(bytes, signature) {
|
|
19
|
+
return signature.every((byte, index) => bytes[index] === byte);
|
|
20
|
+
}
|
|
21
|
+
/** The text of an upload: UTF-16 by its byte order mark, else UTF-8, else Latin-1 (as Windows code pages read). */
|
|
22
|
+
function decodeText(bytes) {
|
|
23
|
+
if (startsWith(bytes, UTF16LE_BOM) || startsWith(bytes, UTF16BE_BOM)) {
|
|
24
|
+
const encoding = startsWith(bytes, UTF16LE_BOM) ? 'utf-16le' : 'utf-16be';
|
|
25
|
+
try {
|
|
26
|
+
return new TextDecoder(encoding, { fatal: true }).decode(bytes);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return '\0';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
return new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(bytes).replace(UTF8_BOM, '');
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString('latin1');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** The cells of a CSV text, unquoted, split on any common delimiter; quoted cells may span lines. */
|
|
40
|
+
function* readCells(text) {
|
|
41
|
+
let position = 0;
|
|
42
|
+
while (position <= text.length) {
|
|
43
|
+
let cell = '';
|
|
44
|
+
if (text[position] === '"') {
|
|
45
|
+
position++;
|
|
46
|
+
for (;;) {
|
|
47
|
+
const close = text.indexOf('"', position);
|
|
48
|
+
if (close < 0) {
|
|
49
|
+
cell += text.slice(position);
|
|
50
|
+
position = text.length;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
cell += text.slice(position, close);
|
|
54
|
+
position = close + 1;
|
|
55
|
+
if (text[position] !== '"') {
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
cell += '"';
|
|
59
|
+
position++;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
CELL_DELIMITERS.lastIndex = position;
|
|
63
|
+
const end = CELL_DELIMITERS.exec(text)?.index ?? text.length;
|
|
64
|
+
yield cell + text.slice(position, end);
|
|
65
|
+
position = end + 1;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** Whether a text upload is text, and for CSV free of formulas that call a function or DDE. */
|
|
69
|
+
export function inspectText(bytes, csv) {
|
|
70
|
+
const text = decodeText(bytes);
|
|
71
|
+
if (CONTROL_CHARACTER.test(text)) {
|
|
72
|
+
return 'mismatch';
|
|
73
|
+
}
|
|
74
|
+
if (csv) {
|
|
75
|
+
for (const cell of readCells(text)) {
|
|
76
|
+
if (FORMULA_START.test(cell) && FUNCTION_CALL_OR_DDE.test(cell)) {
|
|
77
|
+
return 'macros';
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return 'valid';
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=text-content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-content.js","sourceRoot":"","sources":["../src/text-content.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Strict allowlist + validation for document uploads: PDF, MS Office (also the
|
|
3
|
-
* legacy formats), LibreOffice, and
|
|
3
|
+
* legacy formats), LibreOffice, photos (JPEG, PNG, WebP, HEIC/HEIF, AVIF) and CSV and text. Validation happens on three axes: file
|
|
4
4
|
* extension, declared MIME type, and the actual content, i.e. the magic-byte
|
|
5
5
|
* signature plus the parts a file of that kind always has. Extension and MIME
|
|
6
6
|
* alone are trivially spoofed.
|
|
7
7
|
*/
|
|
8
|
-
export type AllowedExtension = 'pdf' | 'doc' | 'docx' | 'ppt' | 'pptx' | 'xls' | 'xlsx' | 'odt' | 'ods' | 'odp' | 'jpg' | 'jpeg' | 'png';
|
|
8
|
+
export type AllowedExtension = 'pdf' | 'doc' | 'docx' | 'ppt' | 'pptx' | 'xls' | 'xlsx' | 'odt' | 'ods' | 'odp' | 'jpg' | 'jpeg' | 'png' | 'webp' | 'heic' | 'heif' | 'avif' | 'csv' | 'txt';
|
|
9
9
|
export declare const ALLOWED_EXTENSIONS: readonly AllowedExtension[];
|
|
10
10
|
/** The `accept` attribute value for a file input that takes `extensions`. */
|
|
11
11
|
export declare function acceptAttribute(extensions?: readonly AllowedExtension[]): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upload-allowlist.d.ts","sourceRoot":"","sources":["../src/upload-allowlist.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"upload-allowlist.d.ts","sourceRoot":"","sources":["../src/upload-allowlist.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH,MAAM,MAAM,gBAAgB,GACzB,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAChF,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;AA4Y9E,eAAO,MAAM,kBAAkB,EAAE,SAAS,gBAAgB,EAAoD,CAAC;AAE/G,6EAA6E;AAC7E,wBAAgB,eAAe,CAAE,UAAU,GAAE,SAAS,gBAAgB,EAAuB,GAAI,MAAM,CAEtG;AAKD,MAAM,WAAW,gBAAgB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,aAAa;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,iBAAiB,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;CAChD;AAED,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,WAAW,GAAG,kBAAkB,GAAG,eAAe,CAAC;AAE/F,MAAM,MAAM,gBAAgB,GACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,gBAAgB,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,GACpE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAA;CAAE,CAAC;AAE9C,wBAAgB,YAAY,CAAE,QAAQ,EAAE,MAAM,GAAI,MAAM,CAMvD;AA4BD,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAAE,QAAQ,EAAE,MAAM,GAAI,MAAM,CAsB3D;AAMD;;;GAGG;AACH,wBAAgB,wBAAwB,CACvC,UAAU,EAAE,gBAAgB,EAC5B,EAAE,QAAQ,EAAE,iBAAsC,EAAE,EAAE,aAAa,GACjE,gBAAgB,CAwBlB;AAED,oEAAoE;AACpE,wBAAgB,oBAAoB,CAAE,SAAS,EAAE,gBAAgB,GAAI,MAAM,CAE1E;AA+BD,wGAAwG;AACxG,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,UAAU,CAAC;AAE/E;;;GAGG;AACH,wBAAgB,kBAAkB,CAAE,SAAS,EAAE,gBAAgB,EAAE,KAAK,EAAE,UAAU,GAAI,kBAAkB,CAwEvG"}
|