upload-safety 0.2.0 → 0.3.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 +41 -7
- package/dist/archive-content.d.ts +15 -0
- package/dist/archive-content.d.ts.map +1 -0
- package/dist/archive-content.js +277 -0
- package/dist/archive-content.js.map +1 -0
- package/dist/check-upload.js +1 -1
- package/dist/check-upload.js.map +1 -1
- package/dist/heif-metadata.d.ts +8 -0
- package/dist/heif-metadata.d.ts.map +1 -1
- package/dist/heif-metadata.js +1 -1
- package/dist/heif-metadata.js.map +1 -1
- package/dist/media-formats.d.ts +45 -0
- package/dist/media-formats.d.ts.map +1 -0
- package/dist/media-formats.js +457 -0
- package/dist/media-formats.js.map +1 -0
- package/dist/photo-metadata.d.ts +4 -2
- package/dist/photo-metadata.d.ts.map +1 -1
- package/dist/photo-metadata.js +29 -2
- package/dist/photo-metadata.js.map +1 -1
- package/dist/rtf-content.d.ts +12 -0
- package/dist/rtf-content.d.ts.map +1 -0
- package/dist/rtf-content.js +175 -0
- package/dist/rtf-content.js.map +1 -0
- package/dist/upload-allowlist.d.ts +19 -8
- package/dist/upload-allowlist.d.ts.map +1 -1
- package/dist/upload-allowlist.js +119 -23
- package/dist/upload-allowlist.js.map +1 -1
- package/dist/video-formats.d.ts +24 -0
- package/dist/video-formats.d.ts.map +1 -0
- package/dist/video-formats.js +269 -0
- package/dist/video-formats.js.map +1 -0
- package/dist/zip-entries.d.ts +8 -0
- package/dist/zip-entries.d.ts.map +1 -1
- package/dist/zip-entries.js +29 -2
- package/dist/zip-entries.js.map +1 -1
- package/package.json +7 -4
- package/src/archive-content.ts +306 -0
- package/src/check-upload.ts +1 -1
- package/src/heif-metadata.ts +2 -2
- package/src/media-formats.ts +478 -0
- package/src/photo-metadata.ts +30 -2
- package/src/rtf-content.ts +178 -0
- package/src/upload-allowlist.ts +145 -27
- package/src/video-formats.ts +287 -0
- package/src/zip-entries.ts +33 -3
package/README.md
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
[](package.json)
|
|
7
7
|
[](package.json)
|
|
8
8
|
[](src/index.ts)
|
|
9
|
+
[](https://upload-safety.simonhalimonov.com)
|
|
9
10
|
|
|
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
|
+
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 and videos, including iPhone HEIC and MOV, lose their GPS position and other metadata on the way through. Riskier formats such as RTF and archives are opt-in, and each format's risk is spelled out.
|
|
11
12
|
|
|
12
13
|
For apps that take CVs, invoices, contracts and photos from strangers and hand them to colleagues who open them in Office or Acrobat.
|
|
13
14
|
|
|
@@ -35,6 +36,9 @@ if ( ! result.ok ) {
|
|
|
35
36
|
| `data.xlsx` that is a zip bomb | refused, `content-mismatch` |
|
|
36
37
|
| `holiday.jpg` with GPS coordinates | accepted, EXIF, XMP, IPTC and comments removed, orientation kept |
|
|
37
38
|
| `IMG_0042.heic` from an iPhone | accepted, EXIF and XMP overwritten in place, pixels unchanged |
|
|
39
|
+
| `clip.mov` from an iPhone with its location | accepted, location, title and other metadata boxes blanked in place |
|
|
40
|
+
| `letter.rtf` with an embedded OLE object or a DDE field | refused, `macros` (RTF is opt-in) |
|
|
41
|
+
| `files.zip` holding a `.docx` with macros or an `.exe` | refused, `macros` or `content-mismatch` (archives are opt-in) |
|
|
38
42
|
| `../../CON.pdf`, a plain PDF | accepted, stored as `_CON.pdf` |
|
|
39
43
|
|
|
40
44
|
Every check fails closed: a part it cannot read is a refusal, not a pass.
|
|
@@ -44,7 +48,9 @@ Every check fails closed: a part it cannot read is a refusal, not a pass.
|
|
|
44
48
|
- **Zero runtime dependencies.** Only `node:zlib` and `node:crypto`. Nothing to audit but this package.
|
|
45
49
|
- **Typed reasons.** Each refusal is one of seven string literals, so a `Record<UploadRejection, string>` of messages is checked by the compiler.
|
|
46
50
|
- **Fast enough to run inline.** Synchronous; 647 real documents up to 20 MB each took at most 65 ms on a laptop.
|
|
47
|
-
- **Tested against crafted files.**
|
|
51
|
+
- **Tested against crafted files.** 372 tests build malformed ZIPs, tars, compound files, PDFs, RTF and media files byte by byte, next to real Office, PDF, photo, audio and video files.
|
|
52
|
+
|
|
53
|
+
Full documentation, with a page per format: **[upload-safety.simonhalimonov.com](https://upload-safety.simonhalimonov.com)**.
|
|
48
54
|
|
|
49
55
|
## Install
|
|
50
56
|
|
|
@@ -136,9 +142,34 @@ app.post( '/upload', upload.single( 'file' ), ( req, res ) => {
|
|
|
136
142
|
| OpenDocument | `odt` `ods` `odp` | The `mimetype` entry; Basic and script modules; embedded OLE objects; event listeners; links to macros; encrypted manifests |
|
|
137
143
|
| 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
144
|
| 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 |
|
|
145
|
+
| Other images | `gif` `bmp` `ico` | GIF: comments, XMP and other application extensions removed, anything after the end cut off. BMP and ICO: headers |
|
|
146
|
+
| Audio | `mp3` `wav` `flac` `ogg` `m4a` | Frame and chunk structure; ID3, APE, RIFF INFO, Vorbis comment and picture blocks and iTunes tags removed. Ogg pages are checked by their CRC, their comments stay |
|
|
147
|
+
| Video | `mp4` `mov` `webm` | Box or element structure; `udta`, `meta`, XMP and `uuid` boxes (iPhone location included), WebM tags, attachments and title blanked in place, so no offset moves |
|
|
139
148
|
| 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 |
|
|
149
|
+
| Markdown | `md` | As text. Opt-in |
|
|
150
|
+
| RTF | `rtf` | Refused on any embedded or linked object (`\obj…`), attached template, data store, or DDE field, however it is spelled. Opt-in |
|
|
151
|
+
| Archives | `zip` `tar` `gz` `tgz` | Every file inside must be of a format in your list (not another archive) and pass its own checks; links, devices, unsafe paths and encrypted entries are refused. Opt-in |
|
|
152
|
+
| RAR, 7z | `rar` `7z` | Signature only. Opt-in |
|
|
153
|
+
|
|
154
|
+
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.
|
|
155
|
+
|
|
156
|
+
## Choosing formats
|
|
140
157
|
|
|
141
|
-
|
|
158
|
+
Each format has a risk level in `FORMAT_RISK`. Only `low` formats are in the default list; the others are accepted only when you name them in `allowedExtensions`.
|
|
159
|
+
|
|
160
|
+
| Risk | Formats | Why |
|
|
161
|
+
| - | - | - |
|
|
162
|
+
| `low` | documents, photos and other images, audio, video, `csv`, `txt` | Read in full; nothing in them runs once the checks pass |
|
|
163
|
+
| `elevated` | `md`, `rtf`, `zip`, `tar`, `gz`, `tgz` | Read in full, but RTF is a common malware carrier whose reader in Word is lenient, Markdown becomes HTML when rendered, and archives get unpacked, and their files keep their metadata |
|
|
164
|
+
| `unchecked` | `rar`, `7z` | Reading them needs a decompressor this package does not have, so anything can be inside. Serve them only as downloads, ideally after a virus scan |
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
import { ALLOWED_EXTENSIONS, checkUpload } from 'upload-safety';
|
|
168
|
+
|
|
169
|
+
checkUpload( file, { maxBytes, allowedExtensions: [ ...ALLOWED_EXTENSIONS, 'zip' ] } );
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Files inside an archive are checked against the same `allowedExtensions`, so a ZIP of PDFs needs `'pdf'` in the list too.
|
|
142
173
|
|
|
143
174
|
## API
|
|
144
175
|
|
|
@@ -148,19 +179,22 @@ ZIP and OLE reading is capped against zip bombs and malformed sector chains. Eve
|
|
|
148
179
|
| - | - |
|
|
149
180
|
| `checkUpload( { filename, mimeType, bytes }, options )` | Every check below; `{ ok: true, extension, filename, mimeType, bytes }` or `{ ok: false, reason }` |
|
|
150
181
|
| `validateUploadDescriptor( { filename, mimeType, size }, options )` | Size, extension and MIME checks before reading the body |
|
|
151
|
-
| `inspectFileContent( extension, bytes )` | `'valid' \| 'macros' \| 'encrypted' \| 'mismatch'` |
|
|
152
|
-
| `stripPhotoMetadata( extension, bytes )` |
|
|
182
|
+
| `inspectFileContent( extension, bytes, allowedExtensions? )` | `'valid' \| 'macros' \| 'encrypted' \| 'mismatch'`; files in an archive must be of a format in `allowedExtensions` |
|
|
183
|
+
| `stripPhotoMetadata( extension, bytes )` | Image, audio or video bytes without metadata, the input for other files, `null` for a broken media file |
|
|
184
|
+
| `FORMAT_RISK`, `ALLOWED_EXTENSIONS` | The risk of each format, and the default list (the `low` ones) |
|
|
153
185
|
| `sanitizeFilename( name )` | Drops path parts, unsafe characters and trailing dots, renames Windows device names such as `CON`, caps the length at 200 UTF-8 bytes |
|
|
154
186
|
| `getCanonicalMimeType( extension )` | The MIME type to store |
|
|
155
187
|
| `acceptAttribute( extensions? )` | Value for `<input type="file" accept>` |
|
|
156
188
|
| `isEncryptedPdf( buffer )` | PDF encryption check alone |
|
|
157
189
|
|
|
158
|
-
`options` takes `maxBytes` and
|
|
190
|
+
`options` takes `maxBytes` and `allowedExtensions`, which defaults to `ALLOWED_EXTENSIONS`. The reason `macros` covers all active content: VBA and Excel 4.0 macros, embedded OLE objects, DDE, event listeners and PDF scripts and attachments.
|
|
159
191
|
|
|
160
192
|
## Limits
|
|
161
193
|
|
|
162
194
|
These checks are not an antivirus scan. They refuse active content and anything they cannot read. They do not judge whether the data in an otherwise plain file is malicious, and they do not look inside a document embedded in another one, such as the workbook behind a Word chart. DDE is not looked for in legacy `.doc` files, nor in an `.xlsx` cell formula that names a DDE source without an external link part.
|
|
163
195
|
|
|
196
|
+
Media checks read the container, not the codec data. GPS that a camera records per frame in a metadata track (GoPro, DJI) stays, as do Ogg comments, and blanked video boxes keep the file at its size. Files inside archives are checked but not rewritten, so photos in a ZIP keep their metadata. `checkUpload` takes the whole file in memory; cap `maxBytes` for video accordingly.
|
|
197
|
+
|
|
164
198
|
The ZIP limits (`MAX_ZIP_UNCOMPRESSED_BYTES`, `MAX_ZIP_ENTRY_UNCOMPRESSED_BYTES`) are set to 100 MB. If a virus scanner runs after these checks, keep them at or below its own limits: ClamAV, for one, reports content past `MaxScanSize` and `MaxFileSize` as clean.
|
|
165
199
|
|
|
166
200
|
## Before and after the checks
|
|
@@ -170,7 +204,7 @@ The library checks the bytes it is handed. The rest of the upload path is up to
|
|
|
170
204
|
- **Limit the request body before reading it.** `maxBytes` is checked after the whole file is in memory, so cap the body in your server or proxy first.
|
|
171
205
|
- **Serve stored files as downloads.** Send `Content-Disposition: attachment` and `X-Content-Type-Options: nosniff`, and use the canonical `result.mimeType`, not the one the client declared. Serving uploads from a separate origin keeps a file that slips through away from your cookies.
|
|
172
206
|
- **Limit image dimensions before resizing.** A small JPEG or PNG can declare a huge size. These checks do not decode images, so cap width and height in whatever makes thumbnails (sharp's `limitInputPixels`, ImageMagick's `policy.xml`).
|
|
173
|
-
- **Expect metadata in documents.** Only
|
|
207
|
+
- **Expect metadata in documents.** Only images, audio and video are stripped. Author names in Office and PDF files, and EXIF in images inside documents, are kept.
|
|
174
208
|
- **Run a virus scanner if you need one.** These checks refuse active content; they do not recognise malware in data.
|
|
175
209
|
|
|
176
210
|
## Development
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads ZIP, TAR and gzip archives as containers and hands every regular file in them to a
|
|
3
|
+
* caller-supplied check. Anything the reader cannot follow exactly (links, devices, unsafe names,
|
|
4
|
+
* extensions it does not model, oversized content) fails closed as 'mismatch'.
|
|
5
|
+
*/
|
|
6
|
+
export type ArchiveVerdict = 'valid' | 'macros' | 'encrypted' | 'mismatch';
|
|
7
|
+
/** Checks one file inside an archive by its name and bytes. */
|
|
8
|
+
export type EntryCheck = (name: string, bytes: Buffer) => ArchiveVerdict;
|
|
9
|
+
/** Checks every regular file of a ZIP archive; password-protected entries give 'encrypted'. */
|
|
10
|
+
export declare function inspectZipArchive(content: Buffer, checkEntry: EntryCheck): ArchiveVerdict;
|
|
11
|
+
/** Checks every regular file of a tar archive. */
|
|
12
|
+
export declare function inspectTarArchive(content: Buffer, checkEntry: EntryCheck): ArchiveVerdict;
|
|
13
|
+
/** Checks the contents of a gzip file: `tarOnly` for .tgz, else a .gz holding a tar or a single file named in its header. */
|
|
14
|
+
export declare function inspectGzip(content: Buffer, checkEntry: EntryCheck, tarOnly: boolean): ArchiveVerdict;
|
|
15
|
+
//# sourceMappingURL=archive-content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archive-content.d.ts","sourceRoot":"","sources":["../src/archive-content.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,UAAU,CAAC;AAE3E,+DAA+D;AAC/D,MAAM,MAAM,UAAU,GAAG,CAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAM,cAAc,CAAC;AAwE3E,+FAA+F;AAC/F,wBAAgB,iBAAiB,CAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAI,cAAc,CA0B3F;AA0ED,kDAAkD;AAClD,wBAAgB,iBAAiB,CAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAI,cAAc,CAoE3F;AA0BD,6HAA6H;AAC7H,wBAAgB,WAAW,CAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,GAAI,cAAc,CAoBvG"}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads ZIP, TAR and gzip archives as containers and hands every regular file in them to a
|
|
3
|
+
* caller-supplied check. Anything the reader cannot follow exactly (links, devices, unsafe names,
|
|
4
|
+
* extensions it does not model, oversized content) fails closed as 'mismatch'.
|
|
5
|
+
*/
|
|
6
|
+
import { gunzipSync } from 'node:zlib';
|
|
7
|
+
import { hasEncryptedZipEntry, isSafeName, MAX_ZIP_ENTRIES, MAX_ZIP_ENTRY_UNCOMPRESSED_BYTES, MAX_ZIP_UNCOMPRESSED_BYTES, readZipEntries, readZipEntryData } from './zip-entries.js';
|
|
8
|
+
const BLOCK_SIZE = 512;
|
|
9
|
+
const TAR_NAME_OFFSET = 0;
|
|
10
|
+
const TAR_NAME_LENGTH = 100;
|
|
11
|
+
const TAR_SIZE_OFFSET = 124;
|
|
12
|
+
const TAR_SIZE_LENGTH = 12;
|
|
13
|
+
const TAR_CHECKSUM_OFFSET = 148;
|
|
14
|
+
const TAR_CHECKSUM_LENGTH = 8;
|
|
15
|
+
const TAR_TYPE_OFFSET = 156;
|
|
16
|
+
const TAR_MAGIC_OFFSET = 257;
|
|
17
|
+
const TAR_PREFIX_OFFSET = 345;
|
|
18
|
+
const TAR_PREFIX_LENGTH = 155;
|
|
19
|
+
const TAR_MAGICS = [Buffer.from('ustar\x0000', 'latin1'), Buffer.from('ustar \0', 'latin1')];
|
|
20
|
+
const TAR_MAGIC_LENGTH = 8;
|
|
21
|
+
/** Longest pax header or GNU long name accepted; real paths are far shorter. */
|
|
22
|
+
const MAX_TAR_NAME_DATA_BYTES = 64 * 1024;
|
|
23
|
+
const OCTAL = /^[0-7]+$/;
|
|
24
|
+
const PAX_RECORD_LENGTH = /^[1-9][0-9]*$/;
|
|
25
|
+
const GZIP_MAGIC = [0x1f, 0x8b];
|
|
26
|
+
const GZIP_DEFLATE_METHOD = 8;
|
|
27
|
+
const GZIP_FIXED_HEADER_SIZE = 10;
|
|
28
|
+
const GZIP_FLAGS_OFFSET = 3;
|
|
29
|
+
const GZIP_FHCRC = 0x02;
|
|
30
|
+
const GZIP_FEXTRA = 0x04;
|
|
31
|
+
const GZIP_FNAME = 0x08;
|
|
32
|
+
const GZIP_FCOMMENT = 0x10;
|
|
33
|
+
const GZIP_RESERVED_FLAGS = 0xe0;
|
|
34
|
+
const APPLE_DOUBLE_MAGIC = Buffer.from([0x00, 0x05, 0x16, 0x07]);
|
|
35
|
+
const DS_STORE_MAGIC = Buffer.from('\0\0\0\x01Bud1', 'latin1');
|
|
36
|
+
const APPLE_DOUBLE_DIRECTORY = '__MACOSX/';
|
|
37
|
+
const APPLE_DOUBLE_PREFIX = '._';
|
|
38
|
+
const DS_STORE_NAME = '.DS_Store';
|
|
39
|
+
function baseName(name) {
|
|
40
|
+
return name.slice(name.search(/[^/\\]*$/));
|
|
41
|
+
}
|
|
42
|
+
/** Whether a file is the macOS metadata Finder adds to archives and its bytes really are that metadata. */
|
|
43
|
+
function isMacJunk(name, bytes) {
|
|
44
|
+
const base = baseName(name);
|
|
45
|
+
if (base === DS_STORE_NAME) {
|
|
46
|
+
return bytes.subarray(0, DS_STORE_MAGIC.length).equals(DS_STORE_MAGIC);
|
|
47
|
+
}
|
|
48
|
+
return name.startsWith(APPLE_DOUBLE_DIRECTORY) && base.startsWith(APPLE_DOUBLE_PREFIX) &&
|
|
49
|
+
bytes.subarray(0, APPLE_DOUBLE_MAGIC.length).equals(APPLE_DOUBLE_MAGIC);
|
|
50
|
+
}
|
|
51
|
+
/** Checks files one at a time, remembering whether any real file was seen and the first failure. */
|
|
52
|
+
class EntryRunner {
|
|
53
|
+
checkEntry;
|
|
54
|
+
files = 0;
|
|
55
|
+
constructor(checkEntry) {
|
|
56
|
+
this.checkEntry = checkEntry;
|
|
57
|
+
}
|
|
58
|
+
/** The verdict for one file, or null while the archive may still pass. */
|
|
59
|
+
run(name, bytes) {
|
|
60
|
+
if (isMacJunk(name, bytes)) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
this.files++;
|
|
64
|
+
const verdict = this.checkEntry(name, bytes);
|
|
65
|
+
return verdict === 'valid' ? null : verdict;
|
|
66
|
+
}
|
|
67
|
+
/** The final verdict once every file has been run. */
|
|
68
|
+
finish() {
|
|
69
|
+
return this.files > 0 ? 'valid' : 'mismatch';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Checks every regular file of a ZIP archive; password-protected entries give 'encrypted'. */
|
|
73
|
+
export function inspectZipArchive(content, checkEntry) {
|
|
74
|
+
const entries = readZipEntries(content);
|
|
75
|
+
if (!entries) {
|
|
76
|
+
return hasEncryptedZipEntry(content) ? 'encrypted' : 'mismatch';
|
|
77
|
+
}
|
|
78
|
+
const runner = new EntryRunner(checkEntry);
|
|
79
|
+
let budget = MAX_ZIP_UNCOMPRESSED_BYTES;
|
|
80
|
+
for (const entry of entries) {
|
|
81
|
+
if (entry.symlink) {
|
|
82
|
+
return 'mismatch';
|
|
83
|
+
}
|
|
84
|
+
if (entry.name.endsWith('/')) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const bytes = readZipEntryData(content, entry, Math.min(budget, MAX_ZIP_ENTRY_UNCOMPRESSED_BYTES));
|
|
88
|
+
if (!bytes) {
|
|
89
|
+
return 'mismatch';
|
|
90
|
+
}
|
|
91
|
+
budget -= bytes.length;
|
|
92
|
+
const verdict = runner.run(entry.name, bytes);
|
|
93
|
+
if (verdict) {
|
|
94
|
+
return verdict;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return runner.finish();
|
|
98
|
+
}
|
|
99
|
+
function isZeroBlock(block) {
|
|
100
|
+
return block.every((byte) => byte === 0);
|
|
101
|
+
}
|
|
102
|
+
/** The text up to the first NUL. */
|
|
103
|
+
function cString(bytes) {
|
|
104
|
+
const end = bytes.indexOf(0);
|
|
105
|
+
return bytes.toString('utf8', 0, end < 0 ? bytes.length : end);
|
|
106
|
+
}
|
|
107
|
+
/** A number in a tar header field, or null unless it is plain octal (base-256 is not supported). */
|
|
108
|
+
function parseOctal(field) {
|
|
109
|
+
const text = field.toString('latin1').replace(/^ +/, '').replace(/[\0 ]+$/, '');
|
|
110
|
+
return OCTAL.test(text) ? parseInt(text, 8) : null;
|
|
111
|
+
}
|
|
112
|
+
function hasValidChecksum(header) {
|
|
113
|
+
const stored = parseOctal(header.subarray(TAR_CHECKSUM_OFFSET, TAR_CHECKSUM_OFFSET + TAR_CHECKSUM_LENGTH));
|
|
114
|
+
if (stored === null) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
let unsigned = 0;
|
|
118
|
+
let signed = 0;
|
|
119
|
+
for (let index = 0; index < BLOCK_SIZE; index++) {
|
|
120
|
+
const inChecksum = index >= TAR_CHECKSUM_OFFSET && index < TAR_CHECKSUM_OFFSET + TAR_CHECKSUM_LENGTH;
|
|
121
|
+
const byte = inChecksum ? 0x20 : header[index];
|
|
122
|
+
unsigned += byte;
|
|
123
|
+
signed += byte > 127 ? byte - 256 : byte;
|
|
124
|
+
}
|
|
125
|
+
return stored === unsigned || stored === signed;
|
|
126
|
+
}
|
|
127
|
+
function hasTarMagic(header) {
|
|
128
|
+
const magic = header.subarray(TAR_MAGIC_OFFSET, TAR_MAGIC_OFFSET + TAR_MAGIC_LENGTH);
|
|
129
|
+
return TAR_MAGICS.some((expected) => expected.equals(magic));
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The `path` of a pax extended header (`<length> <key>=<value>\n` records), '' when it has none,
|
|
133
|
+
* or null when the header is malformed or sets something this reader does not model.
|
|
134
|
+
*/
|
|
135
|
+
function parsePaxPath(data) {
|
|
136
|
+
let path = '';
|
|
137
|
+
let position = 0;
|
|
138
|
+
while (position < data.length) {
|
|
139
|
+
const space = data.indexOf(0x20, position);
|
|
140
|
+
const lengthText = space < 0 ? '' : data.toString('latin1', position, space);
|
|
141
|
+
if (!PAX_RECORD_LENGTH.test(lengthText)) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
const end = position + Number(lengthText);
|
|
145
|
+
if (end > data.length || end <= space + 1 || data[end - 1] !== 0x0a) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
const record = data.toString('utf8', space + 1, end - 1);
|
|
149
|
+
const equals = record.indexOf('=');
|
|
150
|
+
if (equals < 0) {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
const key = record.slice(0, equals);
|
|
154
|
+
// A pax size would change where the next header starts.
|
|
155
|
+
if (key === 'size') {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
if (key === 'path') {
|
|
159
|
+
path = record.slice(equals + 1);
|
|
160
|
+
}
|
|
161
|
+
position = end;
|
|
162
|
+
}
|
|
163
|
+
return path;
|
|
164
|
+
}
|
|
165
|
+
/** Checks every regular file of a tar archive. */
|
|
166
|
+
export function inspectTarArchive(content, checkEntry) {
|
|
167
|
+
const runner = new EntryRunner(checkEntry);
|
|
168
|
+
let position = 0;
|
|
169
|
+
let headers = 0;
|
|
170
|
+
let totalBytes = 0;
|
|
171
|
+
let nextName = null;
|
|
172
|
+
for (;;) {
|
|
173
|
+
if (position === content.length) {
|
|
174
|
+
return nextName === null ? runner.finish() : 'mismatch';
|
|
175
|
+
}
|
|
176
|
+
const header = content.subarray(position, position + BLOCK_SIZE);
|
|
177
|
+
if (header.length < BLOCK_SIZE) {
|
|
178
|
+
return 'mismatch';
|
|
179
|
+
}
|
|
180
|
+
if (isZeroBlock(header)) {
|
|
181
|
+
// Nothing but padding may follow the end marker.
|
|
182
|
+
return nextName === null && content.subarray(position).every((byte) => byte === 0) ? runner.finish() : 'mismatch';
|
|
183
|
+
}
|
|
184
|
+
if (++headers > MAX_ZIP_ENTRIES || !hasTarMagic(header) || !hasValidChecksum(header)) {
|
|
185
|
+
return 'mismatch';
|
|
186
|
+
}
|
|
187
|
+
const size = parseOctal(header.subarray(TAR_SIZE_OFFSET, TAR_SIZE_OFFSET + TAR_SIZE_LENGTH));
|
|
188
|
+
const type = String.fromCharCode(header[TAR_TYPE_OFFSET]);
|
|
189
|
+
const dataStart = position + BLOCK_SIZE;
|
|
190
|
+
const next = dataStart + Math.ceil((size ?? 0) / BLOCK_SIZE) * BLOCK_SIZE;
|
|
191
|
+
if (size === null || size > MAX_ZIP_ENTRY_UNCOMPRESSED_BYTES || next > content.length) {
|
|
192
|
+
return 'mismatch';
|
|
193
|
+
}
|
|
194
|
+
const data = content.subarray(dataStart, dataStart + size);
|
|
195
|
+
position = next;
|
|
196
|
+
if (type === 'g') {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (type === 'x' || type === 'L') {
|
|
200
|
+
const name = type === 'x' ? parsePaxPath(data) : cString(data);
|
|
201
|
+
if (name === null || size > MAX_TAR_NAME_DATA_BYTES) {
|
|
202
|
+
return 'mismatch';
|
|
203
|
+
}
|
|
204
|
+
// An empty pax path leaves a pending long name in force.
|
|
205
|
+
nextName = name || nextName;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
const prefix = cString(header.subarray(TAR_PREFIX_OFFSET, TAR_PREFIX_OFFSET + TAR_PREFIX_LENGTH));
|
|
209
|
+
const ownName = cString(header.subarray(TAR_NAME_OFFSET, TAR_NAME_OFFSET + TAR_NAME_LENGTH));
|
|
210
|
+
const name = nextName ?? (prefix ? `${prefix}/${ownName}` : ownName);
|
|
211
|
+
nextName = null;
|
|
212
|
+
const isDirectory = type === '5' || ((type === '0' || type === '\0') && name.endsWith('/') && size === 0);
|
|
213
|
+
if (!isSafeName(name)) {
|
|
214
|
+
return 'mismatch';
|
|
215
|
+
}
|
|
216
|
+
if (isDirectory) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (type !== '0' && type !== '\0') {
|
|
220
|
+
return 'mismatch';
|
|
221
|
+
}
|
|
222
|
+
totalBytes += size;
|
|
223
|
+
if (totalBytes > MAX_ZIP_UNCOMPRESSED_BYTES) {
|
|
224
|
+
return 'mismatch';
|
|
225
|
+
}
|
|
226
|
+
const verdict = runner.run(name, data);
|
|
227
|
+
if (verdict) {
|
|
228
|
+
return verdict;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/** The FNAME of a gzip header, '' when it has none, or null when the header is malformed. */
|
|
233
|
+
function readGzipName(content) {
|
|
234
|
+
if (content.length < GZIP_FIXED_HEADER_SIZE || content[0] !== GZIP_MAGIC[0] || content[1] !== GZIP_MAGIC[1] ||
|
|
235
|
+
content[2] !== GZIP_DEFLATE_METHOD) {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
const flags = content[GZIP_FLAGS_OFFSET];
|
|
239
|
+
if (flags & GZIP_RESERVED_FLAGS) {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
let position = GZIP_FIXED_HEADER_SIZE;
|
|
243
|
+
if (flags & GZIP_FEXTRA) {
|
|
244
|
+
if (position + 2 > content.length) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
position += 2 + content.readUInt16LE(position);
|
|
248
|
+
}
|
|
249
|
+
if (!(flags & GZIP_FNAME)) {
|
|
250
|
+
return '';
|
|
251
|
+
}
|
|
252
|
+
const end = content.indexOf(0, position);
|
|
253
|
+
return position <= end ? content.toString('latin1', position, end) : null;
|
|
254
|
+
}
|
|
255
|
+
/** Checks the contents of a gzip file: `tarOnly` for .tgz, else a .gz holding a tar or a single file named in its header. */
|
|
256
|
+
export function inspectGzip(content, checkEntry, tarOnly) {
|
|
257
|
+
const name = readGzipName(content);
|
|
258
|
+
if (name === null) {
|
|
259
|
+
return 'mismatch';
|
|
260
|
+
}
|
|
261
|
+
let data;
|
|
262
|
+
try {
|
|
263
|
+
data = gunzipSync(content, { maxOutputLength: MAX_ZIP_UNCOMPRESSED_BYTES });
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
return 'mismatch';
|
|
267
|
+
}
|
|
268
|
+
if (data.length >= BLOCK_SIZE && hasTarMagic(data.subarray(0, BLOCK_SIZE))) {
|
|
269
|
+
return inspectTarArchive(data, checkEntry);
|
|
270
|
+
}
|
|
271
|
+
const file = baseName(name);
|
|
272
|
+
if (tarOnly || !file || !isSafeName(file)) {
|
|
273
|
+
return 'mismatch';
|
|
274
|
+
}
|
|
275
|
+
return checkEntry(file, data);
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=archive-content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archive-content.js","sourceRoot":"","sources":["../src/archive-content.ts"],"names":[],"mappings":"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"}
|
package/dist/check-upload.js
CHANGED
|
@@ -7,7 +7,7 @@ export function checkUpload({ filename, mimeType, bytes }, options) {
|
|
|
7
7
|
return descriptor;
|
|
8
8
|
}
|
|
9
9
|
const { extension, sanitizedFilename } = descriptor;
|
|
10
|
-
const verdict = inspectFileContent(extension, bytes);
|
|
10
|
+
const verdict = inspectFileContent(extension, bytes, options.allowedExtensions);
|
|
11
11
|
if (verdict === 'macros' || verdict === 'encrypted') {
|
|
12
12
|
return { ok: false, reason: verdict };
|
|
13
13
|
}
|
package/dist/check-upload.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-upload.js","sourceRoot":"","sources":["../src/check-upload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACN,oBAAoB,EACpB,kBAAkB,EAClB,wBAAwB,GAIxB,MAAM,uBAAuB,CAAC;AAuB/B,uEAAuE;AACvE,MAAM,UAAU,WAAW,CAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAe,EAAE,OAAsB;IAC9F,MAAM,UAAU,GAAG,wBAAwB,CAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,EAAE,OAAO,CAAE,CAAC;IACvG,IAAK,CAAE,UAAU,CAAC,EAAE,EAAG,CAAC;QACvB,OAAO,UAAU,CAAC;IACnB,CAAC;IACD,MAAM,EAAE,SAAS,EAAE,iBAAiB,EAAE,GAAG,UAAU,CAAC;IACpD,MAAM,OAAO,GAAG,kBAAkB,CAAE,SAAS,EAAE,KAAK,CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"check-upload.js","sourceRoot":"","sources":["../src/check-upload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACN,oBAAoB,EACpB,kBAAkB,EAClB,wBAAwB,GAIxB,MAAM,uBAAuB,CAAC;AAuB/B,uEAAuE;AACvE,MAAM,UAAU,WAAW,CAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAe,EAAE,OAAsB;IAC9F,MAAM,UAAU,GAAG,wBAAwB,CAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,EAAE,OAAO,CAAE,CAAC;IACvG,IAAK,CAAE,UAAU,CAAC,EAAE,EAAG,CAAC;QACvB,OAAO,UAAU,CAAC;IACnB,CAAC;IACD,MAAM,EAAE,SAAS,EAAE,iBAAiB,EAAE,GAAG,UAAU,CAAC;IACpD,MAAM,OAAO,GAAG,kBAAkB,CAAE,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,iBAAiB,CAAE,CAAC;IAClF,IAAK,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,WAAW,EAAG,CAAC;QACvD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IACvC,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAE,SAAS,EAAE,KAAK,CAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACnF,IAAK,CAAE,MAAM,EAAG,CAAC;QAChB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAClD,CAAC;IACD,OAAO;QACN,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,iBAAiB,EAAE,QAAQ,EAAE,oBAAoB,CAAE,SAAS,CAAE,EAAE,KAAK,EAAE,MAAM;KAC5G,CAAC;AACH,CAAC"}
|
package/dist/heif-metadata.d.ts
CHANGED
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
* place instead: EXIF with an empty TIFF block, XMP with spaces. The orientation lives in the
|
|
5
5
|
* `irot` and `imir` properties, which stay. Image sequences (`moov`) are refused.
|
|
6
6
|
*/
|
|
7
|
+
export interface Box {
|
|
8
|
+
type: string;
|
|
9
|
+
/** Where the box's content starts, past its header. */
|
|
10
|
+
start: number;
|
|
11
|
+
end: number;
|
|
12
|
+
}
|
|
13
|
+
/** The boxes in `[start, end)`, or null when one runs past it or there are too many. */
|
|
14
|
+
export declare function readBoxes(bytes: Buffer, start: number, end: number): Box[] | null;
|
|
7
15
|
/**
|
|
8
16
|
* The HEIF image with its EXIF and XMP items overwritten, or null when its boxes do not read,
|
|
9
17
|
* an item's data cannot be located, or it is an image sequence.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"heif-metadata.d.ts","sourceRoot":"","sources":["../src/heif-metadata.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"heif-metadata.d.ts","sourceRoot":"","sources":["../src/heif-metadata.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,GAAG;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACZ;AAWD,wFAAwF;AACxF,wBAAgB,SAAS,CAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAI,GAAG,EAAE,GAAG,IAAI,CA4BnF;AA4HD;;;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"}
|
package/dist/heif-metadata.js
CHANGED
|
@@ -12,7 +12,7 @@ const XMP_CONTENT_TYPE = 'application/rdf+xml';
|
|
|
12
12
|
const EMPTY_EXIF = Buffer.from('00000000' + '4d4d002a00000008' + '0000' + '00000000', 'hex');
|
|
13
13
|
const SPACE = 0x20;
|
|
14
14
|
/** The boxes in `[start, end)`, or null when one runs past it or there are too many. */
|
|
15
|
-
function readBoxes(bytes, start, end) {
|
|
15
|
+
export function readBoxes(bytes, start, end) {
|
|
16
16
|
const boxes = [];
|
|
17
17
|
for (let offset = start; offset < end;) {
|
|
18
18
|
if (offset + 8 > end || boxes.length === MAX_BOXES) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"heif-metadata.js","sourceRoot":"","sources":["../src/heif-metadata.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,iEAAiE;AACjE,MAAM,SAAS,GAAG,MAAM,CAAC;AACzB,MAAM,SAAS,GAAG,OAAO,CAAC;AAE1B,MAAM,gBAAgB,GAAG,qBAAqB,CAAC;AAC/C,wGAAwG;AACxG,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAE,UAAU,GAAG,kBAAkB,GAAG,MAAM,GAAG,UAAU,EAAE,KAAK,CAAE,CAAC;AAC/F,MAAM,KAAK,GAAG,IAAI,CAAC;AAEnB,wFAAwF;AACxF,
|
|
1
|
+
{"version":3,"file":"heif-metadata.js","sourceRoot":"","sources":["../src/heif-metadata.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks and cleans GIF, BMP and ICO images and MP3, WAV, FLAC and Ogg audio without decoding
|
|
3
|
+
* them, in the way `photo-metadata.ts` handles photos: GIF blocks, WAV chunks and FLAC metadata
|
|
4
|
+
* blocks are kept only when known, so comments, XMP and tags are dropped whole while the image
|
|
5
|
+
* and audio data stay byte for byte. BMP, ICO and Ogg only get a structure check: BMP and ICO
|
|
6
|
+
* hold no metadata, and Ogg comments (Vorbis, Opus tags) live inside packets that can span
|
|
7
|
+
* pages, so they stay in the file.
|
|
8
|
+
*
|
|
9
|
+
* Every function returns the bytes to store, or null when the structure does not read.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* The GIF without comments, plain text and application extensions other than looping and the colour
|
|
13
|
+
* profile, or null when its blocks do not read or it holds no image. Bytes after the trailer are cut
|
|
14
|
+
* off, so a GIF cannot double as an archive; a missing trailer is added when the data ended cleanly.
|
|
15
|
+
*/
|
|
16
|
+
export declare function stripGif(content: Buffer): Buffer | null;
|
|
17
|
+
/** The BMP unchanged when its headers read, null otherwise. */
|
|
18
|
+
export declare function checkBmp(content: Buffer): Buffer | null;
|
|
19
|
+
/** The ICO unchanged when its directory and each image's header read, null otherwise; cursors (CUR) are refused. */
|
|
20
|
+
export declare function checkIco(content: Buffer): Buffer | null;
|
|
21
|
+
/**
|
|
22
|
+
* The MP3 audio without ID3v2, ID3v1, APEv2 and Lyrics3 tags, or null when it does not start with a valid
|
|
23
|
+
* MPEG frame that is followed by another one (or the end). Only the first two frame headers are checked, so
|
|
24
|
+
* a damaged stream further in is not noticed.
|
|
25
|
+
*/
|
|
26
|
+
export declare function stripMp3(content: Buffer): Buffer | null;
|
|
27
|
+
/**
|
|
28
|
+
* The WAV with only its `fmt `, `fact` and `data` chunks (LIST/INFO, id3, bext, iXML, XMP and the rest are
|
|
29
|
+
* dropped), or null when the chunks do not read or `data` does not follow `fmt `. Bytes after the RIFF
|
|
30
|
+
* size are cut off.
|
|
31
|
+
*/
|
|
32
|
+
export declare function stripWav(content: Buffer): Buffer | null;
|
|
33
|
+
/**
|
|
34
|
+
* The FLAC with only its STREAMINFO, SEEKTABLE and CUESHEET blocks (padding, applications, Vorbis comments and
|
|
35
|
+
* pictures are dropped), or null when the blocks do not read or the audio does not start with a frame sync.
|
|
36
|
+
* Whatever follows the frames, such as a trailing tag, is left as it is.
|
|
37
|
+
*/
|
|
38
|
+
export declare function stripFlac(content: Buffer): Buffer | null;
|
|
39
|
+
/**
|
|
40
|
+
* The Ogg unchanged when the file is a run of whole pages with correct checksums starting with a begin-of-stream
|
|
41
|
+
* page, null otherwise. Comments (Vorbis, Opus tags) sit inside packets that can span pages, so they are not
|
|
42
|
+
* removed; bytes after the last page make the file unreadable instead of being cut.
|
|
43
|
+
*/
|
|
44
|
+
export declare function checkOgg(content: Buffer): Buffer | null;
|
|
45
|
+
//# sourceMappingURL=media-formats.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-formats.d.ts","sourceRoot":"","sources":["../src/media-formats.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAgHH;;;;GAIG;AACH,wBAAgB,QAAQ,CAAE,OAAO,EAAE,MAAM,GAAI,MAAM,GAAG,IAAI,CAsDzD;AAED,+DAA+D;AAC/D,wBAAgB,QAAQ,CAAE,OAAO,EAAE,MAAM,GAAI,MAAM,GAAG,IAAI,CAkBzD;AAED,oHAAoH;AACpH,wBAAgB,QAAQ,CAAE,OAAO,EAAE,MAAM,GAAI,MAAM,GAAG,IAAI,CAuBzD;AAwFD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAE,OAAO,EAAE,MAAM,GAAI,MAAM,GAAG,IAAI,CAYzD;AAUD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAE,OAAO,EAAE,MAAM,GAAI,MAAM,GAAG,IAAI,CAqCzD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAE,OAAO,EAAE,MAAM,GAAI,MAAM,GAAG,IAAI,CAiC1D;AAoBD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAE,OAAO,EAAE,MAAM,GAAI,MAAM,GAAG,IAAI,CA4BzD"}
|