mirror of
https://github.com/nodejs/node.git
synced 2026-10-29 02:35:20 +08:00
Add ZIP archive support to the node:zlib module through three classes and a set of helpers: - ZipEntry: a single archive member, with buffered reads (content()), bounded-memory streaming reads (contentIterator()), and create()/createStream() for building members. - ZipFile: random access to an archive backed by a file descriptor, reading members lazily without retaining their content and writing new members in place; opened with open()/openSync(). - ZipBuffer: a zero-copy, in-memory view over an archive already held in a Buffer. createZipArchive() serializes a sequence of entries into an archive byte stream, and setMaxZipContentSize() bounds the default in-memory decompression size. Every operation has both an asynchronous and a synchronous form. Signed-off-by: Philipp Dunkel <pip@pipobscure.com> PR-URL: https://github.com/nodejs/node/pull/64339 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com> Reviewed-By: Aviv Keller <me@aviv.sh>
88 lines
2.7 KiB
JavaScript
88 lines
2.7 KiB
JavaScript
'use strict';
|
|
|
|
// Low-level binary helpers: bounds-checked archive ranges, safe 64-bit
|
|
// integer read/write, and buffer coercion of user input.
|
|
|
|
const {
|
|
BigInt,
|
|
Number,
|
|
NumberIsInteger,
|
|
} = primordials;
|
|
|
|
const {
|
|
codes: {
|
|
ERR_INVALID_ARG_TYPE,
|
|
ERR_ZIP_INVALID_ARCHIVE,
|
|
},
|
|
} = require('internal/errors');
|
|
const {
|
|
isAnyArrayBuffer,
|
|
isArrayBufferView,
|
|
isUint8Array,
|
|
} = require('internal/util/types');
|
|
const { Buffer } = require('buffer');
|
|
const { BIGINT_MAX_SAFE_INTEGER } = require('internal/zip/constants');
|
|
|
|
// Reject an [offset, offset + length) slice that escapes the archive buffer
|
|
// before it is used to read a record: guards every offset taken from archive
|
|
// bytes against corrupt or hostile values.
|
|
function validateArchiveRange(buffer, offset, length, what) {
|
|
if (
|
|
!NumberIsInteger(offset) ||
|
|
offset < 0 ||
|
|
!NumberIsInteger(length) ||
|
|
length < 0 ||
|
|
offset + length > buffer.length
|
|
) {
|
|
throw new ERR_ZIP_INVALID_ARCHIVE(`${what} is out of bounds`);
|
|
}
|
|
}
|
|
|
|
// Read a little-endian u64 (sizes/offsets, Zip64) that must land in the JS
|
|
// safe-integer range; a field past the buffer or beyond that range means a
|
|
// corrupt or hostile archive.
|
|
function readSafeUint64(buffer, offset) {
|
|
if (offset + 8 > buffer.length) {
|
|
throw new ERR_ZIP_INVALID_ARCHIVE('64-bit field is out of bounds');
|
|
}
|
|
const value = buffer.readBigUInt64LE(offset);
|
|
if (value > BIGINT_MAX_SAFE_INTEGER) {
|
|
throw new ERR_ZIP_INVALID_ARCHIVE('64-bit field exceeds the safe integer range');
|
|
}
|
|
return Number(value);
|
|
}
|
|
|
|
// Write a JS number as a little-endian u64; the write paths only feed values
|
|
// already bounded by the safe-integer range, so no range check is needed here.
|
|
function writeSafeUint64(buffer, offset, value) {
|
|
buffer.writeBigUInt64LE(BigInt(value), offset);
|
|
}
|
|
|
|
|
|
// Coerce user-supplied binary input to a Buffer, aliasing the same memory
|
|
// (no copy) for a TypedArray/DataView/ArrayBuffer and rejecting anything else.
|
|
// internal/crypto/util.js's getArrayBufferOrView() covers similar coercion but
|
|
// returns the view unchanged (and accepts strings with an encoding); this
|
|
// helper exists because the ZIP code needs an actual Buffer over that memory.
|
|
function toBuffer(value, name) {
|
|
if (isUint8Array(value)) {
|
|
return Buffer.isBuffer(value) ?
|
|
value : Buffer.from(value.buffer, value.byteOffset, value.byteLength);
|
|
}
|
|
if (isArrayBufferView(value)) {
|
|
return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
|
|
}
|
|
if (isAnyArrayBuffer(value)) {
|
|
return Buffer.from(value);
|
|
}
|
|
throw new ERR_INVALID_ARG_TYPE(
|
|
name, ['Buffer', 'TypedArray', 'DataView', 'ArrayBuffer'], value);
|
|
}
|
|
|
|
module.exports = {
|
|
validateArchiveRange,
|
|
readSafeUint64,
|
|
writeSafeUint64,
|
|
toBuffer,
|
|
};
|