From 06df3608de25d9cf069fe4e60bcd8bf36f229456 Mon Sep 17 00:00:00 2001 From: Piotr Fusik Date: Wed, 10 Apr 2024 13:13:39 +0200 Subject: [PATCH 1/5] Avoid a redirect to verification keys Fix #591 --- dist/index.js | 2 +- src/gpg.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/index.js b/dist/index.js index 3d011b75..9a698f63 100644 --- a/dist/index.js +++ b/dist/index.js @@ -78,7 +78,7 @@ const core = __importStar(__nccwpck_require__(2186)); const toolCache = __importStar(__nccwpck_require__(7784)); async function setupKeys() { core.debug("Fetching verification keys"); - let path = await toolCache.downloadTool("https://swift.org/keys/all-keys.asc"); + let path = await toolCache.downloadTool("https://www.swift.org/keys/all-keys.asc"); core.debug("Importing verification keys"); await (0, exec_1.exec)(`gpg --import "${path}"`); core.debug("Refreshing keys"); diff --git a/src/gpg.ts b/src/gpg.ts index 8a69ad87..367833d2 100644 --- a/src/gpg.ts +++ b/src/gpg.ts @@ -5,7 +5,7 @@ import * as toolCache from "@actions/tool-cache"; export async function setupKeys() { core.debug("Fetching verification keys"); let path = await toolCache.downloadTool( - "https://swift.org/keys/all-keys.asc" + "https://www.swift.org/keys/all-keys.asc" ); core.debug("Importing verification keys"); From dffb58a1a5b94b0d24347108a77cbeb8937a2e7d Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Tue, 9 Apr 2024 09:24:28 +0200 Subject: [PATCH 2/5] Add workaround for intermittently gzipped gpg keys --- dist/502.index.js | 3208 +++++++++++++++++++++++++++++++++++++++++++++ dist/index.js | 156 +++ package-lock.json | 230 +++- package.json | 1 + src/gpg.ts | 9 + 5 files changed, 3600 insertions(+), 4 deletions(-) create mode 100644 dist/502.index.js diff --git a/dist/502.index.js b/dist/502.index.js new file mode 100644 index 00000000..17e71a51 --- /dev/null +++ b/dist/502.index.js @@ -0,0 +1,3208 @@ +exports.id = 502; +exports.ids = [502]; +exports.modules = { + +/***/ 3092: +/***/ ((__unused_webpack_module, exports) => { + +/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */ +exports.i = function (buffer, offset, isLE, mLen, nBytes) { + var e, m + var eLen = (nBytes * 8) - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var nBits = -7 + var i = isLE ? (nBytes - 1) : 0 + var d = isLE ? -1 : 1 + var s = buffer[offset + i] + + i += d + + e = s & ((1 << (-nBits)) - 1) + s >>= (-nBits) + nBits += eLen + for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {} + + m = e & ((1 << (-nBits)) - 1) + e >>= (-nBits) + nBits += mLen + for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {} + + if (e === 0) { + e = 1 - eBias + } else if (e === eMax) { + return m ? NaN : ((s ? -1 : 1) * Infinity) + } else { + m = m + Math.pow(2, mLen) + e = e - eBias + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen) +} + +exports.c = function (buffer, value, offset, isLE, mLen, nBytes) { + var e, m, c + var eLen = (nBytes * 8) - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0) + var i = isLE ? 0 : (nBytes - 1) + var d = isLE ? 1 : -1 + var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 + + value = Math.abs(value) + + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0 + e = eMax + } else { + e = Math.floor(Math.log(value) / Math.LN2) + if (value * (c = Math.pow(2, -e)) < 1) { + e-- + c *= 2 + } + if (e + eBias >= 1) { + value += rt / c + } else { + value += rt * Math.pow(2, 1 - eBias) + } + if (value * c >= 2) { + e++ + c /= 2 + } + + if (e + eBias >= eMax) { + m = 0 + e = eMax + } else if (e + eBias >= 1) { + m = ((value * c) - 1) * Math.pow(2, mLen) + e = e + eBias + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) + e = 0 + } + } + + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} + + e = (e << mLen) | m + eLen += mLen + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} + + buffer[offset + i - d] |= s * 128 +} + + +/***/ }), + +/***/ 502: +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +"use strict"; +// ESM COMPAT FLAG +__webpack_require__.r(__webpack_exports__); + +// EXPORTS +__webpack_require__.d(__webpack_exports__, { + "FileTypeParser": () => (/* reexport */ FileTypeParser), + "fileTypeFromBlob": () => (/* reexport */ fileTypeFromBlob), + "fileTypeFromBuffer": () => (/* reexport */ fileTypeFromBuffer), + "fileTypeFromFile": () => (/* binding */ fileTypeFromFile), + "fileTypeFromStream": () => (/* reexport */ fileTypeFromStream), + "fileTypeFromTokenizer": () => (/* reexport */ fileTypeFromTokenizer), + "fileTypeStream": () => (/* reexport */ fileTypeStream), + "supportedExtensions": () => (/* reexport */ supportedExtensions), + "supportedMimeTypes": () => (/* reexport */ supportedMimeTypes) +}); + +// EXTERNAL MODULE: external "node:fs" +var external_node_fs_ = __webpack_require__(7561); +;// CONCATENATED MODULE: ./node_modules/strtok3/lib/FsPromise.js +/** + * Module convert fs functions to promise based functions + */ + +const pathExists = external_node_fs_.existsSync; +const createReadStream = external_node_fs_.createReadStream; +async function FsPromise_stat(path) { + return new Promise((resolve, reject) => { + external_node_fs_.stat(path, (err, stats) => { + if (err) + reject(err); + else + resolve(stats); + }); + }); +} +async function FsPromise_close(fd) { + return new Promise((resolve, reject) => { + external_node_fs_.close(fd, err => { + if (err) + reject(err); + else + resolve(); + }); + }); +} +async function FsPromise_open(path, mode) { + return new Promise((resolve, reject) => { + external_node_fs_.open(path, mode, (err, fd) => { + if (err) + reject(err); + else + resolve(fd); + }); + }); +} +async function read(fd, buffer, offset, length, position) { + return new Promise((resolve, reject) => { + external_node_fs_.read(fd, buffer, offset, length, position, (err, bytesRead, _buffer) => { + if (err) + reject(err); + else + resolve({ bytesRead, buffer: _buffer }); + }); + }); +} +async function writeFile(path, data) { + return new Promise((resolve, reject) => { + fs.writeFile(path, data, err => { + if (err) + reject(err); + else + resolve(); + }); + }); +} +function writeFileSync(path, data) { + fs.writeFileSync(path, data); +} +async function readFile(path) { + return new Promise((resolve, reject) => { + fs.readFile(path, (err, buffer) => { + if (err) + reject(err); + else + resolve(buffer); + }); + }); +} + +;// CONCATENATED MODULE: ./node_modules/peek-readable/lib/EndOfFileStream.js +const defaultMessages = 'End-Of-Stream'; +/** + * Thrown on read operation of the end of file or stream has been reached + */ +class EndOfStreamError extends Error { + constructor() { + super(defaultMessages); + } +} + +;// CONCATENATED MODULE: ./node_modules/peek-readable/lib/Deferred.js +class Deferred { + constructor() { + this.resolve = () => null; + this.reject = () => null; + this.promise = new Promise((resolve, reject) => { + this.reject = reject; + this.resolve = resolve; + }); + } +} + +;// CONCATENATED MODULE: ./node_modules/peek-readable/lib/StreamReader.js + + + +const maxStreamReadSize = 1 * 1024 * 1024; // Maximum request length on read-stream operation +class StreamReader { + constructor(s) { + this.s = s; + /** + * Deferred used for postponed read request (as not data is yet available to read) + */ + this.deferred = null; + this.endOfStream = false; + /** + * Store peeked data + * @type {Array} + */ + this.peekQueue = []; + if (!s.read || !s.once) { + throw new Error('Expected an instance of stream.Readable'); + } + this.s.once('end', () => this.reject(new EndOfStreamError())); + this.s.once('error', err => this.reject(err)); + this.s.once('close', () => this.reject(new Error('Stream closed'))); + } + /** + * Read ahead (peek) from stream. Subsequent read or peeks will return the same data + * @param uint8Array - Uint8Array (or Buffer) to store data read from stream in + * @param offset - Offset target + * @param length - Number of bytes to read + * @returns Number of bytes peeked + */ + async peek(uint8Array, offset, length) { + const bytesRead = await this.read(uint8Array, offset, length); + this.peekQueue.push(uint8Array.subarray(offset, offset + bytesRead)); // Put read data back to peek buffer + return bytesRead; + } + /** + * Read chunk from stream + * @param buffer - Target Uint8Array (or Buffer) to store data read from stream in + * @param offset - Offset target + * @param length - Number of bytes to read + * @returns Number of bytes read + */ + async read(buffer, offset, length) { + if (length === 0) { + return 0; + } + if (this.peekQueue.length === 0 && this.endOfStream) { + throw new EndOfStreamError(); + } + let remaining = length; + let bytesRead = 0; + // consume peeked data first + while (this.peekQueue.length > 0 && remaining > 0) { + const peekData = this.peekQueue.pop(); // Front of queue + if (!peekData) + throw new Error('peekData should be defined'); + const lenCopy = Math.min(peekData.length, remaining); + buffer.set(peekData.subarray(0, lenCopy), offset + bytesRead); + bytesRead += lenCopy; + remaining -= lenCopy; + if (lenCopy < peekData.length) { + // remainder back to queue + this.peekQueue.push(peekData.subarray(lenCopy)); + } + } + // continue reading from stream if required + while (remaining > 0 && !this.endOfStream) { + const reqLen = Math.min(remaining, maxStreamReadSize); + const chunkLen = await this.readFromStream(buffer, offset + bytesRead, reqLen); + bytesRead += chunkLen; + if (chunkLen < reqLen) + break; + remaining -= chunkLen; + } + return bytesRead; + } + /** + * Read chunk from stream + * @param buffer Target Uint8Array (or Buffer) to store data read from stream in + * @param offset Offset target + * @param length Number of bytes to read + * @returns Number of bytes read + */ + async readFromStream(buffer, offset, length) { + const readBuffer = this.s.read(length); + if (readBuffer) { + buffer.set(readBuffer, offset); + return readBuffer.length; + } + else { + const request = { + buffer, + offset, + length, + deferred: new Deferred() + }; + this.deferred = request.deferred; + this.s.once('readable', () => { + this.readDeferred(request); + }); + return request.deferred.promise; + } + } + /** + * Process deferred read request + * @param request Deferred read request + */ + readDeferred(request) { + const readBuffer = this.s.read(request.length); + if (readBuffer) { + request.buffer.set(readBuffer, request.offset); + request.deferred.resolve(readBuffer.length); + this.deferred = null; + } + else { + this.s.once('readable', () => { + this.readDeferred(request); + }); + } + } + reject(err) { + this.endOfStream = true; + if (this.deferred) { + this.deferred.reject(err); + this.deferred = null; + } + } +} + +;// CONCATENATED MODULE: ./node_modules/peek-readable/lib/index.js + + + +// EXTERNAL MODULE: external "node:buffer" +var external_node_buffer_ = __webpack_require__(2254); +;// CONCATENATED MODULE: ./node_modules/strtok3/lib/AbstractTokenizer.js + + +/** + * Core tokenizer + */ +class AbstractTokenizer { + constructor(fileInfo) { + /** + * Tokenizer-stream position + */ + this.position = 0; + this.numBuffer = new Uint8Array(8); + this.fileInfo = fileInfo ? fileInfo : {}; + } + /** + * Read a token from the tokenizer-stream + * @param token - The token to read + * @param position - If provided, the desired position in the tokenizer-stream + * @returns Promise with token data + */ + async readToken(token, position = this.position) { + const uint8Array = external_node_buffer_.Buffer.alloc(token.len); + const len = await this.readBuffer(uint8Array, { position }); + if (len < token.len) + throw new EndOfStreamError(); + return token.get(uint8Array, 0); + } + /** + * Peek a token from the tokenizer-stream. + * @param token - Token to peek from the tokenizer-stream. + * @param position - Offset where to begin reading within the file. If position is null, data will be read from the current file position. + * @returns Promise with token data + */ + async peekToken(token, position = this.position) { + const uint8Array = external_node_buffer_.Buffer.alloc(token.len); + const len = await this.peekBuffer(uint8Array, { position }); + if (len < token.len) + throw new EndOfStreamError(); + return token.get(uint8Array, 0); + } + /** + * Read a numeric token from the stream + * @param token - Numeric token + * @returns Promise with number + */ + async readNumber(token) { + const len = await this.readBuffer(this.numBuffer, { length: token.len }); + if (len < token.len) + throw new EndOfStreamError(); + return token.get(this.numBuffer, 0); + } + /** + * Read a numeric token from the stream + * @param token - Numeric token + * @returns Promise with number + */ + async peekNumber(token) { + const len = await this.peekBuffer(this.numBuffer, { length: token.len }); + if (len < token.len) + throw new EndOfStreamError(); + return token.get(this.numBuffer, 0); + } + /** + * Ignore number of bytes, advances the pointer in under tokenizer-stream. + * @param length - Number of bytes to ignore + * @return resolves the number of bytes ignored, equals length if this available, otherwise the number of bytes available + */ + async ignore(length) { + if (this.fileInfo.size !== undefined) { + const bytesLeft = this.fileInfo.size - this.position; + if (length > bytesLeft) { + this.position += bytesLeft; + return bytesLeft; + } + } + this.position += length; + return length; + } + async close() { + // empty + } + normalizeOptions(uint8Array, options) { + if (options && options.position !== undefined && options.position < this.position) { + throw new Error('`options.position` must be equal or greater than `tokenizer.position`'); + } + if (options) { + return { + mayBeLess: options.mayBeLess === true, + offset: options.offset ? options.offset : 0, + length: options.length ? options.length : (uint8Array.length - (options.offset ? options.offset : 0)), + position: options.position ? options.position : this.position + }; + } + return { + mayBeLess: false, + offset: 0, + length: uint8Array.length, + position: this.position + }; + } +} + +;// CONCATENATED MODULE: ./node_modules/strtok3/lib/ReadStreamTokenizer.js + + +const maxBufferSize = 256000; +class ReadStreamTokenizer extends AbstractTokenizer { + constructor(stream, fileInfo) { + super(fileInfo); + this.streamReader = new StreamReader(stream); + } + /** + * Get file information, an HTTP-client may implement this doing a HEAD request + * @return Promise with file information + */ + async getFileInfo() { + return this.fileInfo; + } + /** + * Read buffer from tokenizer + * @param uint8Array - Target Uint8Array to fill with data read from the tokenizer-stream + * @param options - Read behaviour options + * @returns Promise with number of bytes read + */ + async readBuffer(uint8Array, options) { + const normOptions = this.normalizeOptions(uint8Array, options); + const skipBytes = normOptions.position - this.position; + if (skipBytes > 0) { + await this.ignore(skipBytes); + return this.readBuffer(uint8Array, options); + } + else if (skipBytes < 0) { + throw new Error('`options.position` must be equal or greater than `tokenizer.position`'); + } + if (normOptions.length === 0) { + return 0; + } + const bytesRead = await this.streamReader.read(uint8Array, normOptions.offset, normOptions.length); + this.position += bytesRead; + if ((!options || !options.mayBeLess) && bytesRead < normOptions.length) { + throw new EndOfStreamError(); + } + return bytesRead; + } + /** + * Peek (read ahead) buffer from tokenizer + * @param uint8Array - Uint8Array (or Buffer) to write data to + * @param options - Read behaviour options + * @returns Promise with number of bytes peeked + */ + async peekBuffer(uint8Array, options) { + const normOptions = this.normalizeOptions(uint8Array, options); + let bytesRead = 0; + if (normOptions.position) { + const skipBytes = normOptions.position - this.position; + if (skipBytes > 0) { + const skipBuffer = new Uint8Array(normOptions.length + skipBytes); + bytesRead = await this.peekBuffer(skipBuffer, { mayBeLess: normOptions.mayBeLess }); + uint8Array.set(skipBuffer.subarray(skipBytes), normOptions.offset); + return bytesRead - skipBytes; + } + else if (skipBytes < 0) { + throw new Error('Cannot peek from a negative offset in a stream'); + } + } + if (normOptions.length > 0) { + try { + bytesRead = await this.streamReader.peek(uint8Array, normOptions.offset, normOptions.length); + } + catch (err) { + if (options && options.mayBeLess && err instanceof EndOfStreamError) { + return 0; + } + throw err; + } + if ((!normOptions.mayBeLess) && bytesRead < normOptions.length) { + throw new EndOfStreamError(); + } + } + return bytesRead; + } + async ignore(length) { + // debug(`ignore ${this.position}...${this.position + length - 1}`); + const bufSize = Math.min(maxBufferSize, length); + const buf = new Uint8Array(bufSize); + let totBytesRead = 0; + while (totBytesRead < length) { + const remaining = length - totBytesRead; + const bytesRead = await this.readBuffer(buf, { length: Math.min(bufSize, remaining) }); + if (bytesRead < 0) { + return bytesRead; + } + totBytesRead += bytesRead; + } + return totBytesRead; + } +} + +;// CONCATENATED MODULE: ./node_modules/strtok3/lib/BufferTokenizer.js + + +class BufferTokenizer extends AbstractTokenizer { + /** + * Construct BufferTokenizer + * @param uint8Array - Uint8Array to tokenize + * @param fileInfo - Pass additional file information to the tokenizer + */ + constructor(uint8Array, fileInfo) { + super(fileInfo); + this.uint8Array = uint8Array; + this.fileInfo.size = this.fileInfo.size ? this.fileInfo.size : uint8Array.length; + } + /** + * Read buffer from tokenizer + * @param uint8Array - Uint8Array to tokenize + * @param options - Read behaviour options + * @returns {Promise} + */ + async readBuffer(uint8Array, options) { + if (options && options.position) { + if (options.position < this.position) { + throw new Error('`options.position` must be equal or greater than `tokenizer.position`'); + } + this.position = options.position; + } + const bytesRead = await this.peekBuffer(uint8Array, options); + this.position += bytesRead; + return bytesRead; + } + /** + * Peek (read ahead) buffer from tokenizer + * @param uint8Array + * @param options - Read behaviour options + * @returns {Promise} + */ + async peekBuffer(uint8Array, options) { + const normOptions = this.normalizeOptions(uint8Array, options); + const bytes2read = Math.min(this.uint8Array.length - normOptions.position, normOptions.length); + if ((!normOptions.mayBeLess) && bytes2read < normOptions.length) { + throw new EndOfStreamError(); + } + else { + uint8Array.set(this.uint8Array.subarray(normOptions.position, normOptions.position + bytes2read), normOptions.offset); + return bytes2read; + } + } + async close() { + // empty + } +} + +;// CONCATENATED MODULE: ./node_modules/strtok3/lib/core.js + + + +/** + * Construct ReadStreamTokenizer from given Stream. + * Will set fileSize, if provided given Stream has set the .path property/ + * @param stream - Read from Node.js Stream.Readable + * @param fileInfo - Pass the file information, like size and MIME-type of the corresponding stream. + * @returns ReadStreamTokenizer + */ +function fromStream(stream, fileInfo) { + fileInfo = fileInfo ? fileInfo : {}; + return new ReadStreamTokenizer(stream, fileInfo); +} +/** + * Construct ReadStreamTokenizer from given Buffer. + * @param uint8Array - Uint8Array to tokenize + * @param fileInfo - Pass additional file information to the tokenizer + * @returns BufferTokenizer + */ +function fromBuffer(uint8Array, fileInfo) { + return new BufferTokenizer(uint8Array, fileInfo); +} + +;// CONCATENATED MODULE: ./node_modules/strtok3/lib/FileTokenizer.js + + + +class FileTokenizer extends AbstractTokenizer { + constructor(fd, fileInfo) { + super(fileInfo); + this.fd = fd; + } + /** + * Read buffer from file + * @param uint8Array - Uint8Array to write result to + * @param options - Read behaviour options + * @returns Promise number of bytes read + */ + async readBuffer(uint8Array, options) { + const normOptions = this.normalizeOptions(uint8Array, options); + this.position = normOptions.position; + const res = await read(this.fd, uint8Array, normOptions.offset, normOptions.length, normOptions.position); + this.position += res.bytesRead; + if (res.bytesRead < normOptions.length && (!options || !options.mayBeLess)) { + throw new EndOfStreamError(); + } + return res.bytesRead; + } + /** + * Peek buffer from file + * @param uint8Array - Uint8Array (or Buffer) to write data to + * @param options - Read behaviour options + * @returns Promise number of bytes read + */ + async peekBuffer(uint8Array, options) { + const normOptions = this.normalizeOptions(uint8Array, options); + const res = await read(this.fd, uint8Array, normOptions.offset, normOptions.length, normOptions.position); + if ((!normOptions.mayBeLess) && res.bytesRead < normOptions.length) { + throw new EndOfStreamError(); + } + return res.bytesRead; + } + async close() { + return FsPromise_close(this.fd); + } +} +async function fromFile(sourceFilePath) { + const stat = await FsPromise_stat(sourceFilePath); + if (!stat.isFile) { + throw new Error(`File not a file: ${sourceFilePath}`); + } + const fd = await FsPromise_open(sourceFilePath, 'r'); + return new FileTokenizer(fd, { path: sourceFilePath, size: stat.size }); +} + +;// CONCATENATED MODULE: ./node_modules/strtok3/lib/index.js + + + + +/** + * Construct ReadStreamTokenizer from given Stream. + * Will set fileSize, if provided given Stream has set the .path property. + * @param stream - Node.js Stream.Readable + * @param fileInfo - Pass additional file information to the tokenizer + * @returns Tokenizer + */ +async function lib_fromStream(stream, fileInfo) { + fileInfo = fileInfo ? fileInfo : {}; + if (stream.path) { + const stat = await fs.stat(stream.path); + fileInfo.path = stream.path; + fileInfo.size = stat.size; + } + return core.fromStream(stream, fileInfo); +} + +// EXTERNAL MODULE: ./node_modules/ieee754/index.js +var ieee754 = __webpack_require__(3092); +;// CONCATENATED MODULE: ./node_modules/token-types/lib/index.js + + +// Primitive types +function dv(array) { + return new DataView(array.buffer, array.byteOffset); +} +/** + * 8-bit unsigned integer + */ +const UINT8 = { + len: 1, + get(array, offset) { + return dv(array).getUint8(offset); + }, + put(array, offset, value) { + dv(array).setUint8(offset, value); + return offset + 1; + } +}; +/** + * 16-bit unsigned integer, Little Endian byte order + */ +const UINT16_LE = { + len: 2, + get(array, offset) { + return dv(array).getUint16(offset, true); + }, + put(array, offset, value) { + dv(array).setUint16(offset, value, true); + return offset + 2; + } +}; +/** + * 16-bit unsigned integer, Big Endian byte order + */ +const UINT16_BE = { + len: 2, + get(array, offset) { + return dv(array).getUint16(offset); + }, + put(array, offset, value) { + dv(array).setUint16(offset, value); + return offset + 2; + } +}; +/** + * 24-bit unsigned integer, Little Endian byte order + */ +const UINT24_LE = { + len: 3, + get(array, offset) { + const dataView = dv(array); + return dataView.getUint8(offset) + (dataView.getUint16(offset + 1, true) << 8); + }, + put(array, offset, value) { + const dataView = dv(array); + dataView.setUint8(offset, value & 0xff); + dataView.setUint16(offset + 1, value >> 8, true); + return offset + 3; + } +}; +/** + * 24-bit unsigned integer, Big Endian byte order + */ +const UINT24_BE = { + len: 3, + get(array, offset) { + const dataView = dv(array); + return (dataView.getUint16(offset) << 8) + dataView.getUint8(offset + 2); + }, + put(array, offset, value) { + const dataView = dv(array); + dataView.setUint16(offset, value >> 8); + dataView.setUint8(offset + 2, value & 0xff); + return offset + 3; + } +}; +/** + * 32-bit unsigned integer, Little Endian byte order + */ +const UINT32_LE = { + len: 4, + get(array, offset) { + return dv(array).getUint32(offset, true); + }, + put(array, offset, value) { + dv(array).setUint32(offset, value, true); + return offset + 4; + } +}; +/** + * 32-bit unsigned integer, Big Endian byte order + */ +const UINT32_BE = { + len: 4, + get(array, offset) { + return dv(array).getUint32(offset); + }, + put(array, offset, value) { + dv(array).setUint32(offset, value); + return offset + 4; + } +}; +/** + * 8-bit signed integer + */ +const INT8 = { + len: 1, + get(array, offset) { + return dv(array).getInt8(offset); + }, + put(array, offset, value) { + dv(array).setInt8(offset, value); + return offset + 1; + } +}; +/** + * 16-bit signed integer, Big Endian byte order + */ +const INT16_BE = { + len: 2, + get(array, offset) { + return dv(array).getInt16(offset); + }, + put(array, offset, value) { + dv(array).setInt16(offset, value); + return offset + 2; + } +}; +/** + * 16-bit signed integer, Little Endian byte order + */ +const INT16_LE = { + len: 2, + get(array, offset) { + return dv(array).getInt16(offset, true); + }, + put(array, offset, value) { + dv(array).setInt16(offset, value, true); + return offset + 2; + } +}; +/** + * 24-bit signed integer, Little Endian byte order + */ +const INT24_LE = { + len: 3, + get(array, offset) { + const unsigned = UINT24_LE.get(array, offset); + return unsigned > 0x7fffff ? unsigned - 0x1000000 : unsigned; + }, + put(array, offset, value) { + const dataView = dv(array); + dataView.setUint8(offset, value & 0xff); + dataView.setUint16(offset + 1, value >> 8, true); + return offset + 3; + } +}; +/** + * 24-bit signed integer, Big Endian byte order + */ +const INT24_BE = { + len: 3, + get(array, offset) { + const unsigned = UINT24_BE.get(array, offset); + return unsigned > 0x7fffff ? unsigned - 0x1000000 : unsigned; + }, + put(array, offset, value) { + const dataView = dv(array); + dataView.setUint16(offset, value >> 8); + dataView.setUint8(offset + 2, value & 0xff); + return offset + 3; + } +}; +/** + * 32-bit signed integer, Big Endian byte order + */ +const INT32_BE = { + len: 4, + get(array, offset) { + return dv(array).getInt32(offset); + }, + put(array, offset, value) { + dv(array).setInt32(offset, value); + return offset + 4; + } +}; +/** + * 32-bit signed integer, Big Endian byte order + */ +const INT32_LE = { + len: 4, + get(array, offset) { + return dv(array).getInt32(offset, true); + }, + put(array, offset, value) { + dv(array).setInt32(offset, value, true); + return offset + 4; + } +}; +/** + * 64-bit unsigned integer, Little Endian byte order + */ +const UINT64_LE = { + len: 8, + get(array, offset) { + return dv(array).getBigUint64(offset, true); + }, + put(array, offset, value) { + dv(array).setBigUint64(offset, value, true); + return offset + 8; + } +}; +/** + * 64-bit signed integer, Little Endian byte order + */ +const INT64_LE = { + len: 8, + get(array, offset) { + return dv(array).getBigInt64(offset, true); + }, + put(array, offset, value) { + dv(array).setBigInt64(offset, value, true); + return offset + 8; + } +}; +/** + * 64-bit unsigned integer, Big Endian byte order + */ +const UINT64_BE = { + len: 8, + get(array, offset) { + return dv(array).getBigUint64(offset); + }, + put(array, offset, value) { + dv(array).setBigUint64(offset, value); + return offset + 8; + } +}; +/** + * 64-bit signed integer, Big Endian byte order + */ +const INT64_BE = { + len: 8, + get(array, offset) { + return dv(array).getBigInt64(offset); + }, + put(array, offset, value) { + dv(array).setBigInt64(offset, value); + return offset + 8; + } +}; +/** + * IEEE 754 16-bit (half precision) float, big endian + */ +const Float16_BE = { + len: 2, + get(dataView, offset) { + return ieee754/* read */.i(dataView, offset, false, 10, this.len); + }, + put(dataView, offset, value) { + ieee754/* write */.c(dataView, value, offset, false, 10, this.len); + return offset + this.len; + } +}; +/** + * IEEE 754 16-bit (half precision) float, little endian + */ +const Float16_LE = { + len: 2, + get(array, offset) { + return ieee754/* read */.i(array, offset, true, 10, this.len); + }, + put(array, offset, value) { + ieee754/* write */.c(array, value, offset, true, 10, this.len); + return offset + this.len; + } +}; +/** + * IEEE 754 32-bit (single precision) float, big endian + */ +const Float32_BE = { + len: 4, + get(array, offset) { + return dv(array).getFloat32(offset); + }, + put(array, offset, value) { + dv(array).setFloat32(offset, value); + return offset + 4; + } +}; +/** + * IEEE 754 32-bit (single precision) float, little endian + */ +const Float32_LE = { + len: 4, + get(array, offset) { + return dv(array).getFloat32(offset, true); + }, + put(array, offset, value) { + dv(array).setFloat32(offset, value, true); + return offset + 4; + } +}; +/** + * IEEE 754 64-bit (double precision) float, big endian + */ +const Float64_BE = { + len: 8, + get(array, offset) { + return dv(array).getFloat64(offset); + }, + put(array, offset, value) { + dv(array).setFloat64(offset, value); + return offset + 8; + } +}; +/** + * IEEE 754 64-bit (double precision) float, little endian + */ +const Float64_LE = { + len: 8, + get(array, offset) { + return dv(array).getFloat64(offset, true); + }, + put(array, offset, value) { + dv(array).setFloat64(offset, value, true); + return offset + 8; + } +}; +/** + * IEEE 754 80-bit (extended precision) float, big endian + */ +const Float80_BE = { + len: 10, + get(array, offset) { + return ieee754/* read */.i(array, offset, false, 63, this.len); + }, + put(array, offset, value) { + ieee754/* write */.c(array, value, offset, false, 63, this.len); + return offset + this.len; + } +}; +/** + * IEEE 754 80-bit (extended precision) float, little endian + */ +const Float80_LE = { + len: 10, + get(array, offset) { + return ieee754/* read */.i(array, offset, true, 63, this.len); + }, + put(array, offset, value) { + ieee754/* write */.c(array, value, offset, true, 63, this.len); + return offset + this.len; + } +}; +/** + * Ignore a given number of bytes + */ +class IgnoreType { + /** + * @param len number of bytes to ignore + */ + constructor(len) { + this.len = len; + } + // ToDo: don't read, but skip data + // eslint-disable-next-line @typescript-eslint/no-empty-function + get(array, off) { + } +} +class Uint8ArrayType { + constructor(len) { + this.len = len; + } + get(array, offset) { + return array.subarray(offset, offset + this.len); + } +} +class BufferType { + constructor(len) { + this.len = len; + } + get(uint8Array, off) { + return Buffer.from(uint8Array.subarray(off, off + this.len)); + } +} +/** + * Consume a fixed number of bytes from the stream and return a string with a specified encoding. + */ +class StringType { + constructor(len, encoding) { + this.len = len; + this.encoding = encoding; + } + get(uint8Array, offset) { + return external_node_buffer_.Buffer.from(uint8Array).toString(this.encoding, offset, offset + this.len); + } +} +/** + * ANSI Latin 1 String + * Using windows-1252 / ISO 8859-1 decoding + */ +class AnsiStringType { + constructor(len) { + this.len = len; + } + static decode(buffer, offset, until) { + let str = ''; + for (let i = offset; i < until; ++i) { + str += AnsiStringType.codePointToString(AnsiStringType.singleByteDecoder(buffer[i])); + } + return str; + } + static inRange(a, min, max) { + return min <= a && a <= max; + } + static codePointToString(cp) { + if (cp <= 0xFFFF) { + return String.fromCharCode(cp); + } + else { + cp -= 0x10000; + return String.fromCharCode((cp >> 10) + 0xD800, (cp & 0x3FF) + 0xDC00); + } + } + static singleByteDecoder(bite) { + if (AnsiStringType.inRange(bite, 0x00, 0x7F)) { + return bite; + } + const codePoint = AnsiStringType.windows1252[bite - 0x80]; + if (codePoint === null) { + throw Error('invaliding encoding'); + } + return codePoint; + } + get(buffer, offset = 0) { + return AnsiStringType.decode(buffer, offset, offset + this.len); + } +} +AnsiStringType.windows1252 = [8364, 129, 8218, 402, 8222, 8230, 8224, 8225, 710, 8240, 352, + 8249, 338, 141, 381, 143, 144, 8216, 8217, 8220, 8221, 8226, 8211, 8212, 732, + 8482, 353, 8250, 339, 157, 382, 376, 160, 161, 162, 163, 164, 165, 166, 167, 168, + 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, + 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255]; + +;// CONCATENATED MODULE: ./node_modules/file-type/util.js +function stringToBytes(string) { + return [...string].map(character => character.charCodeAt(0)); // eslint-disable-line unicorn/prefer-code-point +} + +/** +Checks whether the TAR checksum is valid. + +@param {Buffer} buffer - The TAR header `[offset ... offset + 512]`. +@param {number} offset - TAR header offset. +@returns {boolean} `true` if the TAR checksum is valid, otherwise `false`. +*/ +function tarHeaderChecksumMatches(buffer, offset = 0) { + const readSum = Number.parseInt(buffer.toString('utf8', 148, 154).replace(/\0.*$/, '').trim(), 8); // Read sum in header + if (Number.isNaN(readSum)) { + return false; + } + + let sum = 8 * 0x20; // Initialize signed bit sum + + for (let index = offset; index < offset + 148; index++) { + sum += buffer[index]; + } + + for (let index = offset + 156; index < offset + 512; index++) { + sum += buffer[index]; + } + + return readSum === sum; +} + +/** +ID3 UINT32 sync-safe tokenizer token. +28 bits (representing up to 256MB) integer, the msb is 0 to avoid "false syncsignals". +*/ +const uint32SyncSafeToken = { + get: (buffer, offset) => (buffer[offset + 3] & 0x7F) | ((buffer[offset + 2]) << 7) | ((buffer[offset + 1]) << 14) | ((buffer[offset]) << 21), + len: 4, +}; + +;// CONCATENATED MODULE: ./node_modules/file-type/supported.js +const extensions = [ + 'jpg', + 'png', + 'apng', + 'gif', + 'webp', + 'flif', + 'xcf', + 'cr2', + 'cr3', + 'orf', + 'arw', + 'dng', + 'nef', + 'rw2', + 'raf', + 'tif', + 'bmp', + 'icns', + 'jxr', + 'psd', + 'indd', + 'zip', + 'tar', + 'rar', + 'gz', + 'bz2', + '7z', + 'dmg', + 'mp4', + 'mid', + 'mkv', + 'webm', + 'mov', + 'avi', + 'mpg', + 'mp2', + 'mp3', + 'm4a', + 'oga', + 'ogg', + 'ogv', + 'opus', + 'flac', + 'wav', + 'spx', + 'amr', + 'pdf', + 'epub', + 'elf', + 'macho', + 'exe', + 'swf', + 'rtf', + 'wasm', + 'woff', + 'woff2', + 'eot', + 'ttf', + 'otf', + 'ico', + 'flv', + 'ps', + 'xz', + 'sqlite', + 'nes', + 'crx', + 'xpi', + 'cab', + 'deb', + 'ar', + 'rpm', + 'Z', + 'lz', + 'cfb', + 'mxf', + 'mts', + 'blend', + 'bpg', + 'docx', + 'pptx', + 'xlsx', + '3gp', + '3g2', + 'j2c', + 'jp2', + 'jpm', + 'jpx', + 'mj2', + 'aif', + 'qcp', + 'odt', + 'ods', + 'odp', + 'xml', + 'mobi', + 'heic', + 'cur', + 'ktx', + 'ape', + 'wv', + 'dcm', + 'ics', + 'glb', + 'pcap', + 'dsf', + 'lnk', + 'alias', + 'voc', + 'ac3', + 'm4v', + 'm4p', + 'm4b', + 'f4v', + 'f4p', + 'f4b', + 'f4a', + 'mie', + 'asf', + 'ogm', + 'ogx', + 'mpc', + 'arrow', + 'shp', + 'aac', + 'mp1', + 'it', + 's3m', + 'xm', + 'ai', + 'skp', + 'avif', + 'eps', + 'lzh', + 'pgp', + 'asar', + 'stl', + 'chm', + '3mf', + 'zst', + 'jxl', + 'vcf', + 'jls', + 'pst', + 'dwg', + 'parquet', + 'class', + 'arj', + 'cpio', + 'ace', + 'avro', + 'icc', + 'fbx', +]; + +const supported_mimeTypes = [ + 'image/jpeg', + 'image/png', + 'image/gif', + 'image/webp', + 'image/flif', + 'image/x-xcf', + 'image/x-canon-cr2', + 'image/x-canon-cr3', + 'image/tiff', + 'image/bmp', + 'image/vnd.ms-photo', + 'image/vnd.adobe.photoshop', + 'application/x-indesign', + 'application/epub+zip', + 'application/x-xpinstall', + 'application/vnd.oasis.opendocument.text', + 'application/vnd.oasis.opendocument.spreadsheet', + 'application/vnd.oasis.opendocument.presentation', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'application/zip', + 'application/x-tar', + 'application/x-rar-compressed', + 'application/gzip', + 'application/x-bzip2', + 'application/x-7z-compressed', + 'application/x-apple-diskimage', + 'application/x-apache-arrow', + 'video/mp4', + 'audio/midi', + 'video/x-matroska', + 'video/webm', + 'video/quicktime', + 'video/vnd.avi', + 'audio/wav', + 'audio/qcelp', + 'audio/x-ms-asf', + 'video/x-ms-asf', + 'application/vnd.ms-asf', + 'video/mpeg', + 'video/3gpp', + 'audio/mpeg', + 'audio/mp4', // RFC 4337 + 'audio/opus', + 'video/ogg', + 'audio/ogg', + 'application/ogg', + 'audio/x-flac', + 'audio/ape', + 'audio/wavpack', + 'audio/amr', + 'application/pdf', + 'application/x-elf', + 'application/x-mach-binary', + 'application/x-msdownload', + 'application/x-shockwave-flash', + 'application/rtf', + 'application/wasm', + 'font/woff', + 'font/woff2', + 'application/vnd.ms-fontobject', + 'font/ttf', + 'font/otf', + 'image/x-icon', + 'video/x-flv', + 'application/postscript', + 'application/eps', + 'application/x-xz', + 'application/x-sqlite3', + 'application/x-nintendo-nes-rom', + 'application/x-google-chrome-extension', + 'application/vnd.ms-cab-compressed', + 'application/x-deb', + 'application/x-unix-archive', + 'application/x-rpm', + 'application/x-compress', + 'application/x-lzip', + 'application/x-cfb', + 'application/x-mie', + 'application/mxf', + 'video/mp2t', + 'application/x-blender', + 'image/bpg', + 'image/j2c', + 'image/jp2', + 'image/jpx', + 'image/jpm', + 'image/mj2', + 'audio/aiff', + 'application/xml', + 'application/x-mobipocket-ebook', + 'image/heif', + 'image/heif-sequence', + 'image/heic', + 'image/heic-sequence', + 'image/icns', + 'image/ktx', + 'application/dicom', + 'audio/x-musepack', + 'text/calendar', + 'text/vcard', + 'model/gltf-binary', + 'application/vnd.tcpdump.pcap', + 'audio/x-dsf', // Non-standard + 'application/x.ms.shortcut', // Invented by us + 'application/x.apple.alias', // Invented by us + 'audio/x-voc', + 'audio/vnd.dolby.dd-raw', + 'audio/x-m4a', + 'image/apng', + 'image/x-olympus-orf', + 'image/x-sony-arw', + 'image/x-adobe-dng', + 'image/x-nikon-nef', + 'image/x-panasonic-rw2', + 'image/x-fujifilm-raf', + 'video/x-m4v', + 'video/3gpp2', + 'application/x-esri-shape', + 'audio/aac', + 'audio/x-it', + 'audio/x-s3m', + 'audio/x-xm', + 'video/MP1S', + 'video/MP2P', + 'application/vnd.sketchup.skp', + 'image/avif', + 'application/x-lzh-compressed', + 'application/pgp-encrypted', + 'application/x-asar', + 'model/stl', + 'application/vnd.ms-htmlhelp', + 'model/3mf', + 'image/jxl', + 'application/zstd', + 'image/jls', + 'application/vnd.ms-outlook', + 'image/vnd.dwg', + 'application/x-parquet', + 'application/java-vm', + 'application/x-arj', + 'application/x-cpio', + 'application/x-ace-compressed', + 'application/avro', + 'application/vnd.iccprofile', + 'application/x.autodesk.fbx', // Invented by us +]; + +;// CONCATENATED MODULE: ./node_modules/file-type/core.js + + + + + + +const minimumBytes = 4100; // A fair amount of file-types are detectable within this range. + +async function fileTypeFromStream(stream) { + return new FileTypeParser().fromStream(stream); +} + +async function fileTypeFromBuffer(input) { + return new FileTypeParser().fromBuffer(input); +} + +async function fileTypeFromBlob(blob) { + return new FileTypeParser().fromBlob(blob); +} + +function _check(buffer, headers, options) { + options = { + offset: 0, + ...options, + }; + + for (const [index, header] of headers.entries()) { + // If a bitmask is set + if (options.mask) { + // If header doesn't equal `buf` with bits masked off + if (header !== (options.mask[index] & buffer[index + options.offset])) { + return false; + } + } else if (header !== buffer[index + options.offset]) { + return false; + } + } + + return true; +} + +async function fileTypeFromTokenizer(tokenizer) { + return new FileTypeParser().fromTokenizer(tokenizer); +} + +class FileTypeParser { + constructor(options) { + this.detectors = options?.customDetectors; + + this.fromTokenizer = this.fromTokenizer.bind(this); + this.fromBuffer = this.fromBuffer.bind(this); + this.parse = this.parse.bind(this); + } + + async fromTokenizer(tokenizer) { + const initialPosition = tokenizer.position; + + for (const detector of this.detectors || []) { + const fileType = await detector(tokenizer); + if (fileType) { + return fileType; + } + + if (initialPosition !== tokenizer.position) { + return undefined; // Cannot proceed scanning of the tokenizer is at an arbitrary position + } + } + + return this.parse(tokenizer); + } + + async fromBuffer(input) { + if (!(input instanceof Uint8Array || input instanceof ArrayBuffer)) { + throw new TypeError(`Expected the \`input\` argument to be of type \`Uint8Array\` or \`Buffer\` or \`ArrayBuffer\`, got \`${typeof input}\``); + } + + const buffer = input instanceof Uint8Array ? input : new Uint8Array(input); + + if (!(buffer?.length > 1)) { + return; + } + + return this.fromTokenizer(fromBuffer(buffer)); + } + + async fromBlob(blob) { + const buffer = await blob.arrayBuffer(); + return this.fromBuffer(new Uint8Array(buffer)); + } + + async fromStream(stream) { + const tokenizer = await fromStream(stream); + try { + return await this.fromTokenizer(tokenizer); + } finally { + await tokenizer.close(); + } + } + + async toDetectionStream(readableStream, options = {}) { + const {default: stream} = await Promise.resolve(/* import() */).then(__webpack_require__.t.bind(__webpack_require__, 4492, 19)); + const {sampleSize = minimumBytes} = options; + + return new Promise((resolve, reject) => { + readableStream.on('error', reject); + + readableStream.once('readable', () => { + (async () => { + try { + // Set up output stream + const pass = new stream.PassThrough(); + const outputStream = stream.pipeline ? stream.pipeline(readableStream, pass, () => {}) : readableStream.pipe(pass); + + // Read the input stream and detect the filetype + const chunk = readableStream.read(sampleSize) ?? readableStream.read() ?? external_node_buffer_.Buffer.alloc(0); + try { + pass.fileType = await this.fromBuffer(chunk); + } catch (error) { + if (error instanceof EndOfStreamError) { + pass.fileType = undefined; + } else { + reject(error); + } + } + + resolve(outputStream); + } catch (error) { + reject(error); + } + })(); + }); + }); + } + + check(header, options) { + return _check(this.buffer, header, options); + } + + checkString(header, options) { + return this.check(stringToBytes(header), options); + } + + async parse(tokenizer) { + this.buffer = external_node_buffer_.Buffer.alloc(minimumBytes); + + // Keep reading until EOF if the file size is unknown. + if (tokenizer.fileInfo.size === undefined) { + tokenizer.fileInfo.size = Number.MAX_SAFE_INTEGER; + } + + this.tokenizer = tokenizer; + + await tokenizer.peekBuffer(this.buffer, {length: 12, mayBeLess: true}); + + // -- 2-byte signatures -- + + if (this.check([0x42, 0x4D])) { + return { + ext: 'bmp', + mime: 'image/bmp', + }; + } + + if (this.check([0x0B, 0x77])) { + return { + ext: 'ac3', + mime: 'audio/vnd.dolby.dd-raw', + }; + } + + if (this.check([0x78, 0x01])) { + return { + ext: 'dmg', + mime: 'application/x-apple-diskimage', + }; + } + + if (this.check([0x4D, 0x5A])) { + return { + ext: 'exe', + mime: 'application/x-msdownload', + }; + } + + if (this.check([0x25, 0x21])) { + await tokenizer.peekBuffer(this.buffer, {length: 24, mayBeLess: true}); + + if ( + this.checkString('PS-Adobe-', {offset: 2}) + && this.checkString(' EPSF-', {offset: 14}) + ) { + return { + ext: 'eps', + mime: 'application/eps', + }; + } + + return { + ext: 'ps', + mime: 'application/postscript', + }; + } + + if ( + this.check([0x1F, 0xA0]) + || this.check([0x1F, 0x9D]) + ) { + return { + ext: 'Z', + mime: 'application/x-compress', + }; + } + + if (this.check([0xC7, 0x71])) { + return { + ext: 'cpio', + mime: 'application/x-cpio', + }; + } + + if (this.check([0x60, 0xEA])) { + return { + ext: 'arj', + mime: 'application/x-arj', + }; + } + + // -- 3-byte signatures -- + + if (this.check([0xEF, 0xBB, 0xBF])) { // UTF-8-BOM + // Strip off UTF-8-BOM + this.tokenizer.ignore(3); + return this.parse(tokenizer); + } + + if (this.check([0x47, 0x49, 0x46])) { + return { + ext: 'gif', + mime: 'image/gif', + }; + } + + if (this.check([0x49, 0x49, 0xBC])) { + return { + ext: 'jxr', + mime: 'image/vnd.ms-photo', + }; + } + + if (this.check([0x1F, 0x8B, 0x8])) { + return { + ext: 'gz', + mime: 'application/gzip', + }; + } + + if (this.check([0x42, 0x5A, 0x68])) { + return { + ext: 'bz2', + mime: 'application/x-bzip2', + }; + } + + if (this.checkString('ID3')) { + await tokenizer.ignore(6); // Skip ID3 header until the header size + const id3HeaderLength = await tokenizer.readToken(uint32SyncSafeToken); + if (tokenizer.position + id3HeaderLength > tokenizer.fileInfo.size) { + // Guess file type based on ID3 header for backward compatibility + return { + ext: 'mp3', + mime: 'audio/mpeg', + }; + } + + await tokenizer.ignore(id3HeaderLength); + return this.fromTokenizer(tokenizer); // Skip ID3 header, recursion + } + + // Musepack, SV7 + if (this.checkString('MP+')) { + return { + ext: 'mpc', + mime: 'audio/x-musepack', + }; + } + + if ( + (this.buffer[0] === 0x43 || this.buffer[0] === 0x46) + && this.check([0x57, 0x53], {offset: 1}) + ) { + return { + ext: 'swf', + mime: 'application/x-shockwave-flash', + }; + } + + // -- 4-byte signatures -- + + // Requires a sample size of 4 bytes + if (this.check([0xFF, 0xD8, 0xFF])) { + if (this.check([0xF7], {offset: 3})) { // JPG7/SOF55, indicating a ISO/IEC 14495 / JPEG-LS file + return { + ext: 'jls', + mime: 'image/jls', + }; + } + + return { + ext: 'jpg', + mime: 'image/jpeg', + }; + } + + if (this.check([0x4F, 0x62, 0x6A, 0x01])) { + return { + ext: 'avro', + mime: 'application/avro', + }; + } + + if (this.checkString('FLIF')) { + return { + ext: 'flif', + mime: 'image/flif', + }; + } + + if (this.checkString('8BPS')) { + return { + ext: 'psd', + mime: 'image/vnd.adobe.photoshop', + }; + } + + if (this.checkString('WEBP', {offset: 8})) { + return { + ext: 'webp', + mime: 'image/webp', + }; + } + + // Musepack, SV8 + if (this.checkString('MPCK')) { + return { + ext: 'mpc', + mime: 'audio/x-musepack', + }; + } + + if (this.checkString('FORM')) { + return { + ext: 'aif', + mime: 'audio/aiff', + }; + } + + if (this.checkString('icns', {offset: 0})) { + return { + ext: 'icns', + mime: 'image/icns', + }; + } + + // Zip-based file formats + // Need to be before the `zip` check + if (this.check([0x50, 0x4B, 0x3, 0x4])) { // Local file header signature + try { + while (tokenizer.position + 30 < tokenizer.fileInfo.size) { + await tokenizer.readBuffer(this.buffer, {length: 30}); + + // https://en.wikipedia.org/wiki/Zip_(file_format)#File_headers + const zipHeader = { + compressedSize: this.buffer.readUInt32LE(18), + uncompressedSize: this.buffer.readUInt32LE(22), + filenameLength: this.buffer.readUInt16LE(26), + extraFieldLength: this.buffer.readUInt16LE(28), + }; + + zipHeader.filename = await tokenizer.readToken(new StringType(zipHeader.filenameLength, 'utf-8')); + await tokenizer.ignore(zipHeader.extraFieldLength); + + // Assumes signed `.xpi` from addons.mozilla.org + if (zipHeader.filename === 'META-INF/mozilla.rsa') { + return { + ext: 'xpi', + mime: 'application/x-xpinstall', + }; + } + + if (zipHeader.filename.endsWith('.rels') || zipHeader.filename.endsWith('.xml')) { + const type = zipHeader.filename.split('/')[0]; + switch (type) { + case '_rels': + break; + case 'word': + return { + ext: 'docx', + mime: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + }; + case 'ppt': + return { + ext: 'pptx', + mime: 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + }; + case 'xl': + return { + ext: 'xlsx', + mime: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + }; + default: + break; + } + } + + if (zipHeader.filename.startsWith('xl/')) { + return { + ext: 'xlsx', + mime: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + }; + } + + if (zipHeader.filename.startsWith('3D/') && zipHeader.filename.endsWith('.model')) { + return { + ext: '3mf', + mime: 'model/3mf', + }; + } + + // The docx, xlsx and pptx file types extend the Office Open XML file format: + // https://en.wikipedia.org/wiki/Office_Open_XML_file_formats + // We look for: + // - one entry named '[Content_Types].xml' or '_rels/.rels', + // - one entry indicating specific type of file. + // MS Office, OpenOffice and LibreOffice may put the parts in different order, so the check should not rely on it. + if (zipHeader.filename === 'mimetype' && zipHeader.compressedSize === zipHeader.uncompressedSize) { + let mimeType = await tokenizer.readToken(new StringType(zipHeader.compressedSize, 'utf-8')); + mimeType = mimeType.trim(); + + switch (mimeType) { + case 'application/epub+zip': + return { + ext: 'epub', + mime: 'application/epub+zip', + }; + case 'application/vnd.oasis.opendocument.text': + return { + ext: 'odt', + mime: 'application/vnd.oasis.opendocument.text', + }; + case 'application/vnd.oasis.opendocument.spreadsheet': + return { + ext: 'ods', + mime: 'application/vnd.oasis.opendocument.spreadsheet', + }; + case 'application/vnd.oasis.opendocument.presentation': + return { + ext: 'odp', + mime: 'application/vnd.oasis.opendocument.presentation', + }; + default: + } + } + + // Try to find next header manually when current one is corrupted + if (zipHeader.compressedSize === 0) { + let nextHeaderIndex = -1; + + while (nextHeaderIndex < 0 && (tokenizer.position < tokenizer.fileInfo.size)) { + await tokenizer.peekBuffer(this.buffer, {mayBeLess: true}); + + nextHeaderIndex = this.buffer.indexOf('504B0304', 0, 'hex'); + // Move position to the next header if found, skip the whole buffer otherwise + await tokenizer.ignore(nextHeaderIndex >= 0 ? nextHeaderIndex : this.buffer.length); + } + } else { + await tokenizer.ignore(zipHeader.compressedSize); + } + } + } catch (error) { + if (!(error instanceof EndOfStreamError)) { + throw error; + } + } + + return { + ext: 'zip', + mime: 'application/zip', + }; + } + + if (this.checkString('OggS')) { + // This is an OGG container + await tokenizer.ignore(28); + const type = external_node_buffer_.Buffer.alloc(8); + await tokenizer.readBuffer(type); + + // Needs to be before `ogg` check + if (_check(type, [0x4F, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64])) { + return { + ext: 'opus', + mime: 'audio/opus', + }; + } + + // If ' theora' in header. + if (_check(type, [0x80, 0x74, 0x68, 0x65, 0x6F, 0x72, 0x61])) { + return { + ext: 'ogv', + mime: 'video/ogg', + }; + } + + // If '\x01video' in header. + if (_check(type, [0x01, 0x76, 0x69, 0x64, 0x65, 0x6F, 0x00])) { + return { + ext: 'ogm', + mime: 'video/ogg', + }; + } + + // If ' FLAC' in header https://xiph.org/flac/faq.html + if (_check(type, [0x7F, 0x46, 0x4C, 0x41, 0x43])) { + return { + ext: 'oga', + mime: 'audio/ogg', + }; + } + + // 'Speex ' in header https://en.wikipedia.org/wiki/Speex + if (_check(type, [0x53, 0x70, 0x65, 0x65, 0x78, 0x20, 0x20])) { + return { + ext: 'spx', + mime: 'audio/ogg', + }; + } + + // If '\x01vorbis' in header + if (_check(type, [0x01, 0x76, 0x6F, 0x72, 0x62, 0x69, 0x73])) { + return { + ext: 'ogg', + mime: 'audio/ogg', + }; + } + + // Default OGG container https://www.iana.org/assignments/media-types/application/ogg + return { + ext: 'ogx', + mime: 'application/ogg', + }; + } + + if ( + this.check([0x50, 0x4B]) + && (this.buffer[2] === 0x3 || this.buffer[2] === 0x5 || this.buffer[2] === 0x7) + && (this.buffer[3] === 0x4 || this.buffer[3] === 0x6 || this.buffer[3] === 0x8) + ) { + return { + ext: 'zip', + mime: 'application/zip', + }; + } + + // + + // File Type Box (https://en.wikipedia.org/wiki/ISO_base_media_file_format) + // It's not required to be first, but it's recommended to be. Almost all ISO base media files start with `ftyp` box. + // `ftyp` box must contain a brand major identifier, which must consist of ISO 8859-1 printable characters. + // Here we check for 8859-1 printable characters (for simplicity, it's a mask which also catches one non-printable character). + if ( + this.checkString('ftyp', {offset: 4}) + && (this.buffer[8] & 0x60) !== 0x00 // Brand major, first character ASCII? + ) { + // They all can have MIME `video/mp4` except `application/mp4` special-case which is hard to detect. + // For some cases, we're specific, everything else falls to `video/mp4` with `mp4` extension. + const brandMajor = this.buffer.toString('binary', 8, 12).replace('\0', ' ').trim(); + switch (brandMajor) { + case 'avif': + case 'avis': + return {ext: 'avif', mime: 'image/avif'}; + case 'mif1': + return {ext: 'heic', mime: 'image/heif'}; + case 'msf1': + return {ext: 'heic', mime: 'image/heif-sequence'}; + case 'heic': + case 'heix': + return {ext: 'heic', mime: 'image/heic'}; + case 'hevc': + case 'hevx': + return {ext: 'heic', mime: 'image/heic-sequence'}; + case 'qt': + return {ext: 'mov', mime: 'video/quicktime'}; + case 'M4V': + case 'M4VH': + case 'M4VP': + return {ext: 'm4v', mime: 'video/x-m4v'}; + case 'M4P': + return {ext: 'm4p', mime: 'video/mp4'}; + case 'M4B': + return {ext: 'm4b', mime: 'audio/mp4'}; + case 'M4A': + return {ext: 'm4a', mime: 'audio/x-m4a'}; + case 'F4V': + return {ext: 'f4v', mime: 'video/mp4'}; + case 'F4P': + return {ext: 'f4p', mime: 'video/mp4'}; + case 'F4A': + return {ext: 'f4a', mime: 'audio/mp4'}; + case 'F4B': + return {ext: 'f4b', mime: 'audio/mp4'}; + case 'crx': + return {ext: 'cr3', mime: 'image/x-canon-cr3'}; + default: + if (brandMajor.startsWith('3g')) { + if (brandMajor.startsWith('3g2')) { + return {ext: '3g2', mime: 'video/3gpp2'}; + } + + return {ext: '3gp', mime: 'video/3gpp'}; + } + + return {ext: 'mp4', mime: 'video/mp4'}; + } + } + + if (this.checkString('MThd')) { + return { + ext: 'mid', + mime: 'audio/midi', + }; + } + + if ( + this.checkString('wOFF') + && ( + this.check([0x00, 0x01, 0x00, 0x00], {offset: 4}) + || this.checkString('OTTO', {offset: 4}) + ) + ) { + return { + ext: 'woff', + mime: 'font/woff', + }; + } + + if ( + this.checkString('wOF2') + && ( + this.check([0x00, 0x01, 0x00, 0x00], {offset: 4}) + || this.checkString('OTTO', {offset: 4}) + ) + ) { + return { + ext: 'woff2', + mime: 'font/woff2', + }; + } + + if (this.check([0xD4, 0xC3, 0xB2, 0xA1]) || this.check([0xA1, 0xB2, 0xC3, 0xD4])) { + return { + ext: 'pcap', + mime: 'application/vnd.tcpdump.pcap', + }; + } + + // Sony DSD Stream File (DSF) + if (this.checkString('DSD ')) { + return { + ext: 'dsf', + mime: 'audio/x-dsf', // Non-standard + }; + } + + if (this.checkString('LZIP')) { + return { + ext: 'lz', + mime: 'application/x-lzip', + }; + } + + if (this.checkString('fLaC')) { + return { + ext: 'flac', + mime: 'audio/x-flac', + }; + } + + if (this.check([0x42, 0x50, 0x47, 0xFB])) { + return { + ext: 'bpg', + mime: 'image/bpg', + }; + } + + if (this.checkString('wvpk')) { + return { + ext: 'wv', + mime: 'audio/wavpack', + }; + } + + if (this.checkString('%PDF')) { + try { + await tokenizer.ignore(1350); + const maxBufferSize = 10 * 1024 * 1024; + const buffer = external_node_buffer_.Buffer.alloc(Math.min(maxBufferSize, tokenizer.fileInfo.size)); + await tokenizer.readBuffer(buffer, {mayBeLess: true}); + + // Check if this is an Adobe Illustrator file + if (buffer.includes(external_node_buffer_.Buffer.from('AIPrivateData'))) { + return { + ext: 'ai', + mime: 'application/postscript', + }; + } + } catch (error) { + // Swallow end of stream error if file is too small for the Adobe AI check + if (!(error instanceof EndOfStreamError)) { + throw error; + } + } + + // Assume this is just a normal PDF + return { + ext: 'pdf', + mime: 'application/pdf', + }; + } + + if (this.check([0x00, 0x61, 0x73, 0x6D])) { + return { + ext: 'wasm', + mime: 'application/wasm', + }; + } + + // TIFF, little-endian type + if (this.check([0x49, 0x49])) { + const fileType = await this.readTiffHeader(false); + if (fileType) { + return fileType; + } + } + + // TIFF, big-endian type + if (this.check([0x4D, 0x4D])) { + const fileType = await this.readTiffHeader(true); + if (fileType) { + return fileType; + } + } + + if (this.checkString('MAC ')) { + return { + ext: 'ape', + mime: 'audio/ape', + }; + } + + // https://github.com/file/file/blob/master/magic/Magdir/matroska + if (this.check([0x1A, 0x45, 0xDF, 0xA3])) { // Root element: EBML + async function readField() { + const msb = await tokenizer.peekNumber(UINT8); + let mask = 0x80; + let ic = 0; // 0 = A, 1 = B, 2 = C, 3 + // = D + + while ((msb & mask) === 0 && mask !== 0) { + ++ic; + mask >>= 1; + } + + const id = external_node_buffer_.Buffer.alloc(ic + 1); + await tokenizer.readBuffer(id); + return id; + } + + async function readElement() { + const id = await readField(); + const lengthField = await readField(); + lengthField[0] ^= 0x80 >> (lengthField.length - 1); + const nrLength = Math.min(6, lengthField.length); // JavaScript can max read 6 bytes integer + return { + id: id.readUIntBE(0, id.length), + len: lengthField.readUIntBE(lengthField.length - nrLength, nrLength), + }; + } + + async function readChildren(children) { + while (children > 0) { + const element = await readElement(); + if (element.id === 0x42_82) { + const rawValue = await tokenizer.readToken(new StringType(element.len, 'utf-8')); + return rawValue.replaceAll(/\00.*$/g, ''); // Return DocType + } + + await tokenizer.ignore(element.len); // ignore payload + --children; + } + } + + const re = await readElement(); + const docType = await readChildren(re.len); + + switch (docType) { + case 'webm': + return { + ext: 'webm', + mime: 'video/webm', + }; + + case 'matroska': + return { + ext: 'mkv', + mime: 'video/x-matroska', + }; + + default: + return; + } + } + + // RIFF file format which might be AVI, WAV, QCP, etc + if (this.check([0x52, 0x49, 0x46, 0x46])) { + if (this.check([0x41, 0x56, 0x49], {offset: 8})) { + return { + ext: 'avi', + mime: 'video/vnd.avi', + }; + } + + if (this.check([0x57, 0x41, 0x56, 0x45], {offset: 8})) { + return { + ext: 'wav', + mime: 'audio/wav', + }; + } + + // QLCM, QCP file + if (this.check([0x51, 0x4C, 0x43, 0x4D], {offset: 8})) { + return { + ext: 'qcp', + mime: 'audio/qcelp', + }; + } + } + + if (this.checkString('SQLi')) { + return { + ext: 'sqlite', + mime: 'application/x-sqlite3', + }; + } + + if (this.check([0x4E, 0x45, 0x53, 0x1A])) { + return { + ext: 'nes', + mime: 'application/x-nintendo-nes-rom', + }; + } + + if (this.checkString('Cr24')) { + return { + ext: 'crx', + mime: 'application/x-google-chrome-extension', + }; + } + + if ( + this.checkString('MSCF') + || this.checkString('ISc(') + ) { + return { + ext: 'cab', + mime: 'application/vnd.ms-cab-compressed', + }; + } + + if (this.check([0xED, 0xAB, 0xEE, 0xDB])) { + return { + ext: 'rpm', + mime: 'application/x-rpm', + }; + } + + if (this.check([0xC5, 0xD0, 0xD3, 0xC6])) { + return { + ext: 'eps', + mime: 'application/eps', + }; + } + + if (this.check([0x28, 0xB5, 0x2F, 0xFD])) { + return { + ext: 'zst', + mime: 'application/zstd', + }; + } + + if (this.check([0x7F, 0x45, 0x4C, 0x46])) { + return { + ext: 'elf', + mime: 'application/x-elf', + }; + } + + if (this.check([0x21, 0x42, 0x44, 0x4E])) { + return { + ext: 'pst', + mime: 'application/vnd.ms-outlook', + }; + } + + if (this.checkString('PAR1')) { + return { + ext: 'parquet', + mime: 'application/x-parquet', + }; + } + + if (this.check([0xCF, 0xFA, 0xED, 0xFE])) { + return { + ext: 'macho', + mime: 'application/x-mach-binary', + }; + } + + // -- 5-byte signatures -- + + if (this.check([0x4F, 0x54, 0x54, 0x4F, 0x00])) { + return { + ext: 'otf', + mime: 'font/otf', + }; + } + + if (this.checkString('#!AMR')) { + return { + ext: 'amr', + mime: 'audio/amr', + }; + } + + if (this.checkString('{\\rtf')) { + return { + ext: 'rtf', + mime: 'application/rtf', + }; + } + + if (this.check([0x46, 0x4C, 0x56, 0x01])) { + return { + ext: 'flv', + mime: 'video/x-flv', + }; + } + + if (this.checkString('IMPM')) { + return { + ext: 'it', + mime: 'audio/x-it', + }; + } + + if ( + this.checkString('-lh0-', {offset: 2}) + || this.checkString('-lh1-', {offset: 2}) + || this.checkString('-lh2-', {offset: 2}) + || this.checkString('-lh3-', {offset: 2}) + || this.checkString('-lh4-', {offset: 2}) + || this.checkString('-lh5-', {offset: 2}) + || this.checkString('-lh6-', {offset: 2}) + || this.checkString('-lh7-', {offset: 2}) + || this.checkString('-lzs-', {offset: 2}) + || this.checkString('-lz4-', {offset: 2}) + || this.checkString('-lz5-', {offset: 2}) + || this.checkString('-lhd-', {offset: 2}) + ) { + return { + ext: 'lzh', + mime: 'application/x-lzh-compressed', + }; + } + + // MPEG program stream (PS or MPEG-PS) + if (this.check([0x00, 0x00, 0x01, 0xBA])) { + // MPEG-PS, MPEG-1 Part 1 + if (this.check([0x21], {offset: 4, mask: [0xF1]})) { + return { + ext: 'mpg', // May also be .ps, .mpeg + mime: 'video/MP1S', + }; + } + + // MPEG-PS, MPEG-2 Part 1 + if (this.check([0x44], {offset: 4, mask: [0xC4]})) { + return { + ext: 'mpg', // May also be .mpg, .m2p, .vob or .sub + mime: 'video/MP2P', + }; + } + } + + if (this.checkString('ITSF')) { + return { + ext: 'chm', + mime: 'application/vnd.ms-htmlhelp', + }; + } + + if (this.check([0xCA, 0xFE, 0xBA, 0xBE])) { + return { + ext: 'class', + mime: 'application/java-vm', + }; + } + + // -- 6-byte signatures -- + + if (this.check([0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00])) { + return { + ext: 'xz', + mime: 'application/x-xz', + }; + } + + if (this.checkString('= 1000 && version <= 1050) { + return { + ext: 'dwg', + mime: 'image/vnd.dwg', + }; + } + } + + if (this.checkString('070707')) { + return { + ext: 'cpio', + mime: 'application/x-cpio', + }; + } + + // -- 7-byte signatures -- + + if (this.checkString('BLENDER')) { + return { + ext: 'blend', + mime: 'application/x-blender', + }; + } + + if (this.checkString('!')) { + await tokenizer.ignore(8); + const string = await tokenizer.readToken(new StringType(13, 'ascii')); + if (string === 'debian-binary') { + return { + ext: 'deb', + mime: 'application/x-deb', + }; + } + + return { + ext: 'ar', + mime: 'application/x-unix-archive', + }; + } + + if (this.checkString('**ACE', {offset: 7})) { + await tokenizer.peekBuffer(this.buffer, {length: 14, mayBeLess: true}); + if (this.checkString('**', {offset: 12})) { + return { + ext: 'ace', + mime: 'application/x-ace-compressed', + }; + } + } + + // -- 8-byte signatures -- + + if (this.check([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A])) { + // APNG format (https://wiki.mozilla.org/APNG_Specification) + // 1. Find the first IDAT (image data) chunk (49 44 41 54) + // 2. Check if there is an "acTL" chunk before the IDAT one (61 63 54 4C) + + // Offset calculated as follows: + // - 8 bytes: PNG signature + // - 4 (length) + 4 (chunk type) + 13 (chunk data) + 4 (CRC): IHDR chunk + + await tokenizer.ignore(8); // ignore PNG signature + + async function readChunkHeader() { + return { + length: await tokenizer.readToken(INT32_BE), + type: await tokenizer.readToken(new StringType(4, 'binary')), + }; + } + + do { + const chunk = await readChunkHeader(); + if (chunk.length < 0) { + return; // Invalid chunk length + } + + switch (chunk.type) { + case 'IDAT': + return { + ext: 'png', + mime: 'image/png', + }; + case 'acTL': + return { + ext: 'apng', + mime: 'image/apng', + }; + default: + await tokenizer.ignore(chunk.length + 4); // Ignore chunk-data + CRC + } + } while (tokenizer.position + 8 < tokenizer.fileInfo.size); + + return { + ext: 'png', + mime: 'image/png', + }; + } + + if (this.check([0x41, 0x52, 0x52, 0x4F, 0x57, 0x31, 0x00, 0x00])) { + return { + ext: 'arrow', + mime: 'application/x-apache-arrow', + }; + } + + if (this.check([0x67, 0x6C, 0x54, 0x46, 0x02, 0x00, 0x00, 0x00])) { + return { + ext: 'glb', + mime: 'model/gltf-binary', + }; + } + + // `mov` format variants + if ( + this.check([0x66, 0x72, 0x65, 0x65], {offset: 4}) // `free` + || this.check([0x6D, 0x64, 0x61, 0x74], {offset: 4}) // `mdat` MJPEG + || this.check([0x6D, 0x6F, 0x6F, 0x76], {offset: 4}) // `moov` + || this.check([0x77, 0x69, 0x64, 0x65], {offset: 4}) // `wide` + ) { + return { + ext: 'mov', + mime: 'video/quicktime', + }; + } + + // -- 9-byte signatures -- + + if (this.check([0x49, 0x49, 0x52, 0x4F, 0x08, 0x00, 0x00, 0x00, 0x18])) { + return { + ext: 'orf', + mime: 'image/x-olympus-orf', + }; + } + + if (this.checkString('gimp xcf ')) { + return { + ext: 'xcf', + mime: 'image/x-xcf', + }; + } + + // -- 12-byte signatures -- + + if (this.check([0x49, 0x49, 0x55, 0x00, 0x18, 0x00, 0x00, 0x00, 0x88, 0xE7, 0x74, 0xD8])) { + return { + ext: 'rw2', + mime: 'image/x-panasonic-rw2', + }; + } + + // ASF_Header_Object first 80 bytes + if (this.check([0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9])) { + async function readHeader() { + const guid = external_node_buffer_.Buffer.alloc(16); + await tokenizer.readBuffer(guid); + return { + id: guid, + size: Number(await tokenizer.readToken(UINT64_LE)), + }; + } + + await tokenizer.ignore(30); + // Search for header should be in first 1KB of file. + while (tokenizer.position + 24 < tokenizer.fileInfo.size) { + const header = await readHeader(); + let payload = header.size - 24; + if (_check(header.id, [0x91, 0x07, 0xDC, 0xB7, 0xB7, 0xA9, 0xCF, 0x11, 0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65])) { + // Sync on Stream-Properties-Object (B7DC0791-A9B7-11CF-8EE6-00C00C205365) + const typeId = external_node_buffer_.Buffer.alloc(16); + payload -= await tokenizer.readBuffer(typeId); + + if (_check(typeId, [0x40, 0x9E, 0x69, 0xF8, 0x4D, 0x5B, 0xCF, 0x11, 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B])) { + // Found audio: + return { + ext: 'asf', + mime: 'audio/x-ms-asf', + }; + } + + if (_check(typeId, [0xC0, 0xEF, 0x19, 0xBC, 0x4D, 0x5B, 0xCF, 0x11, 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B])) { + // Found video: + return { + ext: 'asf', + mime: 'video/x-ms-asf', + }; + } + + break; + } + + await tokenizer.ignore(payload); + } + + // Default to ASF generic extension + return { + ext: 'asf', + mime: 'application/vnd.ms-asf', + }; + } + + if (this.check([0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A])) { + return { + ext: 'ktx', + mime: 'image/ktx', + }; + } + + if ((this.check([0x7E, 0x10, 0x04]) || this.check([0x7E, 0x18, 0x04])) && this.check([0x30, 0x4D, 0x49, 0x45], {offset: 4})) { + return { + ext: 'mie', + mime: 'application/x-mie', + }; + } + + if (this.check([0x27, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], {offset: 2})) { + return { + ext: 'shp', + mime: 'application/x-esri-shape', + }; + } + + if (this.check([0xFF, 0x4F, 0xFF, 0x51])) { + return { + ext: 'j2c', + mime: 'image/j2c', + }; + } + + if (this.check([0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A])) { + // JPEG-2000 family + + await tokenizer.ignore(20); + const type = await tokenizer.readToken(new StringType(4, 'ascii')); + switch (type) { + case 'jp2 ': + return { + ext: 'jp2', + mime: 'image/jp2', + }; + case 'jpx ': + return { + ext: 'jpx', + mime: 'image/jpx', + }; + case 'jpm ': + return { + ext: 'jpm', + mime: 'image/jpm', + }; + case 'mjp2': + return { + ext: 'mj2', + mime: 'image/mj2', + }; + default: + return; + } + } + + if ( + this.check([0xFF, 0x0A]) + || this.check([0x00, 0x00, 0x00, 0x0C, 0x4A, 0x58, 0x4C, 0x20, 0x0D, 0x0A, 0x87, 0x0A]) + ) { + return { + ext: 'jxl', + mime: 'image/jxl', + }; + } + + if (this.check([0xFE, 0xFF])) { // UTF-16-BOM-LE + if (this.check([0, 60, 0, 63, 0, 120, 0, 109, 0, 108], {offset: 2})) { + return { + ext: 'xml', + mime: 'application/xml', + }; + } + + return undefined; // Some unknown text based format + } + + // -- Unsafe signatures -- + + if ( + this.check([0x0, 0x0, 0x1, 0xBA]) + || this.check([0x0, 0x0, 0x1, 0xB3]) + ) { + return { + ext: 'mpg', + mime: 'video/mpeg', + }; + } + + if (this.check([0x00, 0x01, 0x00, 0x00, 0x00])) { + return { + ext: 'ttf', + mime: 'font/ttf', + }; + } + + if (this.check([0x00, 0x00, 0x01, 0x00])) { + return { + ext: 'ico', + mime: 'image/x-icon', + }; + } + + if (this.check([0x00, 0x00, 0x02, 0x00])) { + return { + ext: 'cur', + mime: 'image/x-icon', + }; + } + + if (this.check([0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1])) { + // Detected Microsoft Compound File Binary File (MS-CFB) Format. + return { + ext: 'cfb', + mime: 'application/x-cfb', + }; + } + + // Increase sample size from 12 to 256. + await tokenizer.peekBuffer(this.buffer, {length: Math.min(256, tokenizer.fileInfo.size), mayBeLess: true}); + + if (this.check([0x61, 0x63, 0x73, 0x70], {offset: 36})) { + return { + ext: 'icc', + mime: 'application/vnd.iccprofile', + }; + } + + // -- 15-byte signatures -- + + if (this.checkString('BEGIN:')) { + if (this.checkString('VCARD', {offset: 6})) { + return { + ext: 'vcf', + mime: 'text/vcard', + }; + } + + if (this.checkString('VCALENDAR', {offset: 6})) { + return { + ext: 'ics', + mime: 'text/calendar', + }; + } + } + + // `raf` is here just to keep all the raw image detectors together. + if (this.checkString('FUJIFILMCCD-RAW')) { + return { + ext: 'raf', + mime: 'image/x-fujifilm-raf', + }; + } + + if (this.checkString('Extended Module:')) { + return { + ext: 'xm', + mime: 'audio/x-xm', + }; + } + + if (this.checkString('Creative Voice File')) { + return { + ext: 'voc', + mime: 'audio/x-voc', + }; + } + + if (this.check([0x04, 0x00, 0x00, 0x00]) && this.buffer.length >= 16) { // Rough & quick check Pickle/ASAR + const jsonSize = this.buffer.readUInt32LE(12); + if (jsonSize > 12 && this.buffer.length >= jsonSize + 16) { + try { + const header = this.buffer.slice(16, jsonSize + 16).toString(); + const json = JSON.parse(header); + // Check if Pickle is ASAR + if (json.files) { // Final check, assuring Pickle/ASAR format + return { + ext: 'asar', + mime: 'application/x-asar', + }; + } + } catch {} + } + } + + if (this.check([0x06, 0x0E, 0x2B, 0x34, 0x02, 0x05, 0x01, 0x01, 0x0D, 0x01, 0x02, 0x01, 0x01, 0x02])) { + return { + ext: 'mxf', + mime: 'application/mxf', + }; + } + + if (this.checkString('SCRM', {offset: 44})) { + return { + ext: 's3m', + mime: 'audio/x-s3m', + }; + } + + // Raw MPEG-2 transport stream (188-byte packets) + if (this.check([0x47]) && this.check([0x47], {offset: 188})) { + return { + ext: 'mts', + mime: 'video/mp2t', + }; + } + + // Blu-ray Disc Audio-Video (BDAV) MPEG-2 transport stream has 4-byte TP_extra_header before each 188-byte packet + if (this.check([0x47], {offset: 4}) && this.check([0x47], {offset: 196})) { + return { + ext: 'mts', + mime: 'video/mp2t', + }; + } + + if (this.check([0x42, 0x4F, 0x4F, 0x4B, 0x4D, 0x4F, 0x42, 0x49], {offset: 60})) { + return { + ext: 'mobi', + mime: 'application/x-mobipocket-ebook', + }; + } + + if (this.check([0x44, 0x49, 0x43, 0x4D], {offset: 128})) { + return { + ext: 'dcm', + mime: 'application/dicom', + }; + } + + if (this.check([0x4C, 0x00, 0x00, 0x00, 0x01, 0x14, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46])) { + return { + ext: 'lnk', + mime: 'application/x.ms.shortcut', // Invented by us + }; + } + + if (this.check([0x62, 0x6F, 0x6F, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x72, 0x6B, 0x00, 0x00, 0x00, 0x00])) { + return { + ext: 'alias', + mime: 'application/x.apple.alias', // Invented by us + }; + } + + if (this.checkString('Kaydara FBX Binary \u0000')) { + return { + ext: 'fbx', + mime: 'application/x.autodesk.fbx', // Invented by us + }; + } + + if ( + this.check([0x4C, 0x50], {offset: 34}) + && ( + this.check([0x00, 0x00, 0x01], {offset: 8}) + || this.check([0x01, 0x00, 0x02], {offset: 8}) + || this.check([0x02, 0x00, 0x02], {offset: 8}) + ) + ) { + return { + ext: 'eot', + mime: 'application/vnd.ms-fontobject', + }; + } + + if (this.check([0x06, 0x06, 0xED, 0xF5, 0xD8, 0x1D, 0x46, 0xE5, 0xBD, 0x31, 0xEF, 0xE7, 0xFE, 0x74, 0xB7, 0x1D])) { + return { + ext: 'indd', + mime: 'application/x-indesign', + }; + } + + // Increase sample size from 256 to 512 + await tokenizer.peekBuffer(this.buffer, {length: Math.min(512, tokenizer.fileInfo.size), mayBeLess: true}); + + // Requires a buffer size of 512 bytes + if (tarHeaderChecksumMatches(this.buffer)) { + return { + ext: 'tar', + mime: 'application/x-tar', + }; + } + + if (this.check([0xFF, 0xFE])) { // UTF-16-BOM-BE + if (this.check([60, 0, 63, 0, 120, 0, 109, 0, 108, 0], {offset: 2})) { + return { + ext: 'xml', + mime: 'application/xml', + }; + } + + if (this.check([0xFF, 0x0E, 0x53, 0x00, 0x6B, 0x00, 0x65, 0x00, 0x74, 0x00, 0x63, 0x00, 0x68, 0x00, 0x55, 0x00, 0x70, 0x00, 0x20, 0x00, 0x4D, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6C, 0x00], {offset: 2})) { + return { + ext: 'skp', + mime: 'application/vnd.sketchup.skp', + }; + } + + return undefined; // Some text based format + } + + if (this.checkString('-----BEGIN PGP MESSAGE-----')) { + return { + ext: 'pgp', + mime: 'application/pgp-encrypted', + }; + } + + // Check MPEG 1 or 2 Layer 3 header, or 'layer 0' for ADTS (MPEG sync-word 0xFFE) + if (this.buffer.length >= 2 && this.check([0xFF, 0xE0], {offset: 0, mask: [0xFF, 0xE0]})) { + if (this.check([0x10], {offset: 1, mask: [0x16]})) { + // Check for (ADTS) MPEG-2 + if (this.check([0x08], {offset: 1, mask: [0x08]})) { + return { + ext: 'aac', + mime: 'audio/aac', + }; + } + + // Must be (ADTS) MPEG-4 + return { + ext: 'aac', + mime: 'audio/aac', + }; + } + + // MPEG 1 or 2 Layer 3 header + // Check for MPEG layer 3 + if (this.check([0x02], {offset: 1, mask: [0x06]})) { + return { + ext: 'mp3', + mime: 'audio/mpeg', + }; + } + + // Check for MPEG layer 2 + if (this.check([0x04], {offset: 1, mask: [0x06]})) { + return { + ext: 'mp2', + mime: 'audio/mpeg', + }; + } + + // Check for MPEG layer 1 + if (this.check([0x06], {offset: 1, mask: [0x06]})) { + return { + ext: 'mp1', + mime: 'audio/mpeg', + }; + } + } + } + + async readTiffTag(bigEndian) { + const tagId = await this.tokenizer.readToken(bigEndian ? UINT16_BE : UINT16_LE); + this.tokenizer.ignore(10); + switch (tagId) { + case 50_341: + return { + ext: 'arw', + mime: 'image/x-sony-arw', + }; + case 50_706: + return { + ext: 'dng', + mime: 'image/x-adobe-dng', + }; + default: + } + } + + async readTiffIFD(bigEndian) { + const numberOfTags = await this.tokenizer.readToken(bigEndian ? UINT16_BE : UINT16_LE); + for (let n = 0; n < numberOfTags; ++n) { + const fileType = await this.readTiffTag(bigEndian); + if (fileType) { + return fileType; + } + } + } + + async readTiffHeader(bigEndian) { + const version = (bigEndian ? UINT16_BE : UINT16_LE).get(this.buffer, 2); + const ifdOffset = (bigEndian ? UINT32_BE : UINT32_LE).get(this.buffer, 4); + + if (version === 42) { + // TIFF file header + if (ifdOffset >= 6) { + if (this.checkString('CR', {offset: 8})) { + return { + ext: 'cr2', + mime: 'image/x-canon-cr2', + }; + } + + if (ifdOffset >= 8 && (this.check([0x1C, 0x00, 0xFE, 0x00], {offset: 8}) || this.check([0x1F, 0x00, 0x0B, 0x00], {offset: 8}))) { + return { + ext: 'nef', + mime: 'image/x-nikon-nef', + }; + } + } + + await this.tokenizer.ignore(ifdOffset); + const fileType = await this.readTiffIFD(bigEndian); + return fileType ?? { + ext: 'tif', + mime: 'image/tiff', + }; + } + + if (version === 43) { // Big TIFF file header + return { + ext: 'tif', + mime: 'image/tiff', + }; + } + } +} + +async function fileTypeStream(readableStream, options = {}) { + return new FileTypeParser().toDetectionStream(readableStream, options); +} + +const supportedExtensions = new Set(extensions); +const supportedMimeTypes = new Set(supported_mimeTypes); + +;// CONCATENATED MODULE: ./node_modules/file-type/index.js + + + +async function fileTypeFromFile(path, fileTypeOptions) { + const tokenizer = await fromFile(path); + try { + const parser = new FileTypeParser(fileTypeOptions); + return await parser.fromTokenizer(tokenizer); + } finally { + await tokenizer.close(); + } +} + + + + +/***/ }) + +}; +; \ No newline at end of file diff --git a/dist/index.js b/dist/index.js index 9a698f63..7b8b368a 100644 --- a/dist/index.js +++ b/dist/index.js @@ -79,6 +79,13 @@ const toolCache = __importStar(__nccwpck_require__(7784)); async function setupKeys() { core.debug("Fetching verification keys"); let path = await toolCache.downloadTool("https://www.swift.org/keys/all-keys.asc"); + const fileTypeModule = await __nccwpck_require__.e(/* import() */ 502).then(__nccwpck_require__.bind(__nccwpck_require__, 502)); + const fileType = await fileTypeModule.fileTypeFromFile(path); + if (fileType && fileType.mime == "application/gzip") { + core.info("Server responded with gzipped data, uncompressing"); + await (0, exec_1.exec)(`mv "${path}" "${path}.gz"`); + await (0, exec_1.exec)(`gunzip "${path}.gz`); + } core.debug("Importing verification keys"); await (0, exec_1.exec)(`gpg --import "${path}"`); core.debug("Refreshing keys"); @@ -17127,6 +17134,30 @@ module.exports = require("net"); /***/ }), +/***/ 2254: +/***/ ((module) => { + +"use strict"; +module.exports = require("node:buffer"); + +/***/ }), + +/***/ 7561: +/***/ ((module) => { + +"use strict"; +module.exports = require("node:fs"); + +/***/ }), + +/***/ 4492: +/***/ ((module) => { + +"use strict"; +module.exports = require("node:stream"); + +/***/ }), + /***/ 2037: /***/ ((module) => { @@ -17223,11 +17254,136 @@ module.exports = JSON.parse('{"/etc/fedora-release":["Fedora"],"/etc/redhat-rele /******/ return module.exports; /******/ } /******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __nccwpck_require__.m = __webpack_modules__; +/******/ /************************************************************************/ +/******/ /* webpack/runtime/create fake namespace object */ +/******/ (() => { +/******/ var getProto = Object.getPrototypeOf ? (obj) => (Object.getPrototypeOf(obj)) : (obj) => (obj.__proto__); +/******/ var leafPrototypes; +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 16: return value when it's Promise-like +/******/ // mode & 8|1: behave like require +/******/ __nccwpck_require__.t = function(value, mode) { +/******/ if(mode & 1) value = this(value); +/******/ if(mode & 8) return value; +/******/ if(typeof value === 'object' && value) { +/******/ if((mode & 4) && value.__esModule) return value; +/******/ if((mode & 16) && typeof value.then === 'function') return value; +/******/ } +/******/ var ns = Object.create(null); +/******/ __nccwpck_require__.r(ns); +/******/ var def = {}; +/******/ leafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)]; +/******/ for(var current = mode & 2 && value; typeof current == 'object' && !~leafPrototypes.indexOf(current); current = getProto(current)) { +/******/ Object.getOwnPropertyNames(current).forEach((key) => (def[key] = () => (value[key]))); +/******/ } +/******/ def['default'] = () => (value); +/******/ __nccwpck_require__.d(ns, def); +/******/ return ns; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __nccwpck_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__nccwpck_require__.o(definition, key) && !__nccwpck_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/ensure chunk */ +/******/ (() => { +/******/ __nccwpck_require__.f = {}; +/******/ // This file contains only the entry chunk. +/******/ // The chunk loading function for additional chunks +/******/ __nccwpck_require__.e = (chunkId) => { +/******/ return Promise.all(Object.keys(__nccwpck_require__.f).reduce((promises, key) => { +/******/ __nccwpck_require__.f[key](chunkId, promises); +/******/ return promises; +/******/ }, [])); +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/get javascript chunk filename */ +/******/ (() => { +/******/ // This function allow to reference async chunks +/******/ __nccwpck_require__.u = (chunkId) => { +/******/ // return url for filenames based on template +/******/ return "" + chunkId + ".index.js"; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __nccwpck_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __nccwpck_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ /******/ /* webpack/runtime/compat */ /******/ /******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/"; /******/ +/******/ /* webpack/runtime/require chunk loading */ +/******/ (() => { +/******/ // no baseURI +/******/ +/******/ // object to store loaded chunks +/******/ // "1" means "loaded", otherwise not loaded yet +/******/ var installedChunks = { +/******/ 179: 1 +/******/ }; +/******/ +/******/ // no on chunks loaded +/******/ +/******/ var installChunk = (chunk) => { +/******/ var moreModules = chunk.modules, chunkIds = chunk.ids, runtime = chunk.runtime; +/******/ for(var moduleId in moreModules) { +/******/ if(__nccwpck_require__.o(moreModules, moduleId)) { +/******/ __nccwpck_require__.m[moduleId] = moreModules[moduleId]; +/******/ } +/******/ } +/******/ if(runtime) runtime(__nccwpck_require__); +/******/ for(var i = 0; i < chunkIds.length; i++) +/******/ installedChunks[chunkIds[i]] = 1; +/******/ +/******/ }; +/******/ +/******/ // require() chunk loading for javascript +/******/ __nccwpck_require__.f.require = (chunkId, promises) => { +/******/ // "1" is the signal for "already loaded" +/******/ if(!installedChunks[chunkId]) { +/******/ if(true) { // all chunks have JS +/******/ installChunk(require("./" + __nccwpck_require__.u(chunkId))); +/******/ } else installedChunks[chunkId] = 1; +/******/ } +/******/ }; +/******/ +/******/ // no external install chunk +/******/ +/******/ // no HMR +/******/ +/******/ // no HMR manifest +/******/ })(); +/******/ /************************************************************************/ /******/ /******/ // startup diff --git a/package-lock.json b/package-lock.json index f4bfdfd8..1c79b583 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@actions/exec": "^1.1.1", "@actions/io": "^1.1.3", "@actions/tool-cache": "^2.0.1", + "file-type": "^19.0.0", "getos": "^3.2.1", "semver": "^7.6.0" }, @@ -1134,6 +1135,11 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1902,6 +1908,22 @@ "bser": "2.1.1" } }, + "node_modules/file-type": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-19.0.0.tgz", + "integrity": "sha512-s7cxa7/leUWLiXO78DVVfBVse+milos9FitauDLG1pI7lNaJ2+5lzPnr2N24ym+84HVwJL6hVuGfgVE+ALvU8Q==", + "dependencies": { + "readable-web-to-node-stream": "^3.0.2", + "strtok3": "^7.0.0", + "token-types": "^5.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -2074,6 +2096,25 @@ "node": ">=10.17.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/import-local": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", @@ -2115,8 +2156,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/is-arrayish": { "version": "0.2.1", @@ -3145,6 +3185,18 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/peek-readable": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", + "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -3260,6 +3312,34 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readable-web-to-node-stream": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", + "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "dependencies": { + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -3316,6 +3396,25 @@ "node": ">=10" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/semver": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", @@ -3409,6 +3508,14 @@ "node": ">=10" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -3478,6 +3585,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strtok3": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", + "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^5.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -3543,6 +3666,22 @@ "node": ">=8.0" } }, + "node_modules/token-types": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", + "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/ts-jest": { "version": "29.1.2", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.2.tgz", @@ -3664,6 +3803,11 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/uuid": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", @@ -4671,6 +4815,11 @@ "@sinonjs/commons": "^3.0.0" } }, + "@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" + }, "@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -5255,6 +5404,16 @@ "bser": "2.1.1" } }, + "file-type": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-19.0.0.tgz", + "integrity": "sha512-s7cxa7/leUWLiXO78DVVfBVse+milos9FitauDLG1pI7lNaJ2+5lzPnr2N24ym+84HVwJL6hVuGfgVE+ALvU8Q==", + "requires": { + "readable-web-to-node-stream": "^3.0.2", + "strtok3": "^7.0.0", + "token-types": "^5.0.1" + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -5378,6 +5537,11 @@ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, "import-local": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", @@ -5407,8 +5571,7 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "is-arrayish": { "version": "0.2.1", @@ -6190,6 +6353,11 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "peek-readable": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", + "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==" + }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -6264,6 +6432,24 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readable-web-to-node-stream": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", + "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "requires": { + "readable-stream": "^3.6.0" + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -6302,6 +6488,11 @@ "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", "dev": true }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, "semver": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", @@ -6374,6 +6565,14 @@ "escape-string-regexp": "^2.0.0" } }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, "string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -6422,6 +6621,15 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, + "strtok3": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", + "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", + "requires": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^5.0.0" + } + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -6469,6 +6677,15 @@ "is-number": "^7.0.0" } }, + "token-types": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", + "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", + "requires": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + } + }, "ts-jest": { "version": "29.1.2", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.2.tgz", @@ -6524,6 +6741,11 @@ "picocolors": "^1.0.0" } }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "uuid": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", diff --git a/package.json b/package.json index 669ed85b..dd922725 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@actions/exec": "^1.1.1", "@actions/io": "^1.1.3", "@actions/tool-cache": "^2.0.1", + "file-type": "^19.0.0", "getos": "^3.2.1", "semver": "^7.6.0" }, diff --git a/src/gpg.ts b/src/gpg.ts index 367833d2..4f03768c 100644 --- a/src/gpg.ts +++ b/src/gpg.ts @@ -8,6 +8,15 @@ export async function setupKeys() { "https://www.swift.org/keys/all-keys.asc" ); + const fileTypeModule = await import("file-type"); + const fileType = await fileTypeModule.fileTypeFromFile(path); + + if (fileType && fileType.mime == "application/gzip") { + core.info("Server responded with gzipped data, uncompressing"); + await exec(`mv "${path}" "${path}.gz"`); + await exec(`gunzip "${path}.gz`); + } + core.debug("Importing verification keys"); await exec(`gpg --import "${path}"`); From 91833fccafc07ba12cd3dc5cbc68212250093b6b Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 29 Aug 2024 01:39:02 +0200 Subject: [PATCH 3/5] Use strict equality check --- src/gpg.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gpg.ts b/src/gpg.ts index 4f03768c..b130ed72 100644 --- a/src/gpg.ts +++ b/src/gpg.ts @@ -11,7 +11,7 @@ export async function setupKeys() { const fileTypeModule = await import("file-type"); const fileType = await fileTypeModule.fileTypeFromFile(path); - if (fileType && fileType.mime == "application/gzip") { + if (fileType && fileType.mime === "application/gzip") { core.info("Server responded with gzipped data, uncompressing"); await exec(`mv "${path}" "${path}.gz"`); await exec(`gunzip "${path}.gz`); From 0c1b94d411dd0f373aa57c420f36075fd6c80cd9 Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 29 Aug 2024 01:42:01 +0200 Subject: [PATCH 4/5] Add link to issue --- src/gpg.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gpg.ts b/src/gpg.ts index b130ed72..5386ea85 100644 --- a/src/gpg.ts +++ b/src/gpg.ts @@ -8,6 +8,7 @@ export async function setupKeys() { "https://www.swift.org/keys/all-keys.asc" ); + // Workaround for https://github.com/swift-actions/setup-swift/issues/591 const fileTypeModule = await import("file-type"); const fileType = await fileTypeModule.fileTypeFromFile(path); From bb83339d1e8577741bdc6c65ba551ce7dc0fb854 Mon Sep 17 00:00:00 2001 From: fwcd Date: Thu, 29 Aug 2024 01:43:06 +0200 Subject: [PATCH 5/5] Only run gpg unzip workaround on macOS and Linux --- dist/index.js | 16 ++++++++++------ src/gpg.ts | 15 +++++++++------ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/dist/index.js b/dist/index.js index 7b8b368a..9da90404 100644 --- a/dist/index.js +++ b/dist/index.js @@ -73,18 +73,22 @@ var __importStar = (this && this.__importStar) || function (mod) { }; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.refreshKeys = exports.verify = exports.setupKeys = void 0; +const os = __importStar(__nccwpck_require__(2037)); const exec_1 = __nccwpck_require__(1514); const core = __importStar(__nccwpck_require__(2186)); const toolCache = __importStar(__nccwpck_require__(7784)); async function setupKeys() { core.debug("Fetching verification keys"); let path = await toolCache.downloadTool("https://www.swift.org/keys/all-keys.asc"); - const fileTypeModule = await __nccwpck_require__.e(/* import() */ 502).then(__nccwpck_require__.bind(__nccwpck_require__, 502)); - const fileType = await fileTypeModule.fileTypeFromFile(path); - if (fileType && fileType.mime == "application/gzip") { - core.info("Server responded with gzipped data, uncompressing"); - await (0, exec_1.exec)(`mv "${path}" "${path}.gz"`); - await (0, exec_1.exec)(`gunzip "${path}.gz`); + // Workaround for https://github.com/swift-actions/setup-swift/issues/591 + if (os.platform() !== "win32") { + const fileTypeModule = await __nccwpck_require__.e(/* import() */ 502).then(__nccwpck_require__.bind(__nccwpck_require__, 502)); + const fileType = await fileTypeModule.fileTypeFromFile(path); + if (fileType && fileType.mime === "application/gzip") { + core.info("Server responded with gzipped data, uncompressing"); + await (0, exec_1.exec)(`mv "${path}" "${path}.gz"`); + await (0, exec_1.exec)(`gunzip "${path}.gz`); + } } core.debug("Importing verification keys"); await (0, exec_1.exec)(`gpg --import "${path}"`); diff --git a/src/gpg.ts b/src/gpg.ts index 5386ea85..6b313ce0 100644 --- a/src/gpg.ts +++ b/src/gpg.ts @@ -1,3 +1,4 @@ +import * as os from "os"; import { exec } from "@actions/exec"; import * as core from "@actions/core"; import * as toolCache from "@actions/tool-cache"; @@ -9,13 +10,15 @@ export async function setupKeys() { ); // Workaround for https://github.com/swift-actions/setup-swift/issues/591 - const fileTypeModule = await import("file-type"); - const fileType = await fileTypeModule.fileTypeFromFile(path); + if (os.platform() !== "win32") { + const fileTypeModule = await import("file-type"); + const fileType = await fileTypeModule.fileTypeFromFile(path); - if (fileType && fileType.mime === "application/gzip") { - core.info("Server responded with gzipped data, uncompressing"); - await exec(`mv "${path}" "${path}.gz"`); - await exec(`gunzip "${path}.gz`); + if (fileType && fileType.mime === "application/gzip") { + core.info("Server responded with gzipped data, uncompressing"); + await exec(`mv "${path}" "${path}.gz"`); + await exec(`gunzip "${path}.gz`); + } } core.debug("Importing verification keys");