From 3e288388b64d3609a3a2c8d08a246124cd274fcc Mon Sep 17 00:00:00 2001 From: Revopush Date: Mon, 18 May 2026 20:54:21 +0300 Subject: [PATCH 01/18] 0.0.12 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6d3b92f..b77b041 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@revopush/code-push-cli", - "version": "0.0.11", + "version": "0.0.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@revopush/code-push-cli", - "version": "0.0.11", + "version": "0.0.12", "dependencies": { "@devicefarmer/adbkit-apkreader": "^3.2.4", "aab-parser": "^1.0.1", diff --git a/package.json b/package.json index 659d43b..a349460 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@revopush/code-push-cli", - "version": "0.0.11", + "version": "0.0.12", "description": "Management CLI for the CodePush service", "main": "./script/cli.js", "scripts": { From f45bd31805c85888b06e9cdb2510d60535dfe043 Mon Sep 17 00:00:00 2001 From: Revopush Date: Thu, 11 Jun 2026 19:46:40 +0300 Subject: [PATCH 02/18] add kts support for android --- script/command-executor.ts | 122 ++------------------------ script/utils/gradle-utils.ts | 164 +++++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 116 deletions(-) create mode 100644 script/utils/gradle-utils.ts diff --git a/script/command-executor.ts b/script/command-executor.ts index 18ec301..0488e2d 100644 --- a/script/command-executor.ts +++ b/script/command-executor.ts @@ -41,6 +41,7 @@ import { takeHermesBaseBytecode, } from "./react-native-utils"; import { fileDoesNotExistOrIsDirectory, fileExists, isBinaryOrZip, extractIPA, extractAPK, extractAAB } from "./utils/file-utils"; +import { getAndroidVersionInfo } from "./utils/gradle-utils"; import AccountManager = require("./management-sdk"); import wordwrap = require("wordwrap"); @@ -48,8 +49,6 @@ import Promise = Q.Promise; import { ReactNativePackageInfo } from "./types/rest-definitions"; import { getExpoCliPath } from "./expo-utils"; -const g2js = require("gradle-to-js/lib/parser"); - const opener = require("opener"); const plist = require("plist"); @@ -66,8 +65,6 @@ const configFilePath: string = path.join(process.env.LOCALAPPDATA || process.env const emailValidator = require("email-validator"); const packageJson = require("../../package.json"); -const properties = require("properties"); - const CLI_HEADERS: Headers = { "X-CodePush-CLI-Version": packageJson.version, }; @@ -940,114 +937,7 @@ function getReactNativeProjectVersionInfo(command: cli.IReleaseReactCommand, pro return Q({ appVersion: rawShortVersion, buildNumber: rawBundleVersion }); } else if (command.platform === "android") { - let buildGradlePath: string = path.join("android", "app"); - if (command.gradleFile) { - buildGradlePath = command.gradleFile; - } - if (fs.lstatSync(buildGradlePath).isDirectory()) { - buildGradlePath = path.join(buildGradlePath, "build.gradle"); - } - - if (fileDoesNotExistOrIsDirectory(buildGradlePath)) { - throw new Error(`Unable to find gradle file "${buildGradlePath}".`); - } - - return g2js - .parseFile(buildGradlePath) - .catch(() => { - throw new Error(`Unable to parse the "${buildGradlePath}" file. Please ensure it is a well-formed Gradle file.`); - }) - .then((buildGradle: any) => { - const warnMissingBuildNumber = () => log(chalk.yellow( - `Warning: Unable to read "android.defaultConfig.versionCode" from "${buildGradlePath}". ` + - `This is expected if it is set dynamically (e.g. on CI). ` + - `Pass --buildNumber explicitly to include it in the release.` - )); - - const knownLocations = [path.join("android", "app", "gradle.properties"), path.join("android", "gradle.properties")]; - - const parsePropertiesFile = (filePath: string): any | null => { - if (!fileExists(filePath)) return null; - try { - return properties.parse(fs.readFileSync(filePath).toString()); - } catch (e) { - throw new Error(`Unable to parse "${filePath}". Please ensure it is a well-formed properties file.`); - } - }; - - let versionName: string | null = null; - let versionCode: string | number | null = null; - - // First 'if' statement was implemented as workaround for case - // when 'build.gradle' file contains several 'android' nodes. - // In this case 'buildGradle.android' prop represents array instead of object - // due to parsing issue in 'g2js.parseFile' method. - if (buildGradle.android instanceof Array) { - for (const gradlePart of buildGradle.android) { - if (gradlePart.defaultConfig) { - versionName = versionName ?? gradlePart.defaultConfig.versionName ?? null; - versionCode = versionCode ?? gradlePart.defaultConfig.versionCode ?? null; - if (versionName !== null && versionCode !== null) break; - } - } - } else if (buildGradle.android && buildGradle.android.defaultConfig) { - versionName = buildGradle.android.defaultConfig.versionName ?? null; - versionCode = buildGradle.android.defaultConfig.versionCode ?? null; - } - - // versionCode may be a direct value or a property reference (non-numeric string) - const versionCodeProperty = typeof versionCode === "string" && !/^\d+$/.test(versionCode) - ? versionCode.replace("project.", "") - : null; - let buildNumber: string | undefined = versionCodeProperty ? undefined : versionCode?.toString(); - - if (!versionName) { - if (!buildNumber) warnMissingBuildNumber(); - return { appVersion: undefined, buildNumber }; - } - - const rawAppVersion = versionName.replace(/"/g, "").trim(); - - if (/^\d/.test(rawAppVersion) && !isValidVersion(rawAppVersion)) { - // Starts with a digit but isn't valid semver — can't be a property reference. - throw new Error( - `The "android.defaultConfig.versionName" property in the "${buildGradlePath}" file needs to specify a valid semver string (e.g. 1.3.2).` - ); - } - - // If versionName isn't a valid semver, treat it as a Gradle property reference - const versionNameProperty: string | null = isValidVersion(rawAppVersion) ? null : rawAppVersion.replace("project.", ""); - let appVersion: string | undefined = versionNameProperty ? undefined : rawAppVersion; - - let resolvedPropertiesFile: string | null = null; - if (versionNameProperty || versionCodeProperty) { - for (const propertiesFile of knownLocations) { - const parsed = parsePropertiesFile(propertiesFile); - if (!parsed) continue; - if (versionNameProperty && !appVersion) { - appVersion = parsed[versionNameProperty]; - if (appVersion) resolvedPropertiesFile = propertiesFile; - } - if (versionCodeProperty && !buildNumber) { - buildNumber = parsed[versionCodeProperty]; - } - if ((!versionNameProperty || appVersion) && (!versionCodeProperty || buildNumber)) break; - } - - if (versionNameProperty && !appVersion) { - throw new Error(`No property named "${versionNameProperty}" exists in the following files: ${knownLocations.join(", ")}.`); - } - if (versionNameProperty && !isValidVersion(appVersion)) { - throw new Error( - `The "${versionNameProperty}" property in the "${resolvedPropertiesFile}" file needs to specify a valid semver string, containing both a major and minor version (e.g. 1.3.2, 1.1).` - ); - } - } - - if (!buildNumber) warnMissingBuildNumber(); - - return { appVersion, buildNumber }; - }); + return Q(getAndroidVersionInfo(command.gradleFile)); } } @@ -1381,10 +1271,10 @@ export const releaseExpo = (command: cli.IReleaseReactCommand): Promise => // For release-expo, buildNumber is NOT auto-detected — it must be passed explicitly // via --buildNumber. Auto-detection only applies to release-native where the binary // build number is the natural targeting key. - const versionInfoPromise: Promise = (command.appStoreVersion && command.buildNumber) + const versionInfoPromise: Promise = command.appStoreVersion ? Q({ appVersion: command.appStoreVersion, buildNumber: command.buildNumber }) : getReactNativeProjectVersionInfo(command, projectName).then((detected) => ({ - appVersion: command.appStoreVersion || detected.appVersion, + appVersion: detected.appVersion, buildNumber: command.buildNumber, })); @@ -1529,10 +1419,10 @@ export const releaseReact = (command: cli.IReleaseReactCommand): Promise = // For release-react, buildNumber is NOT auto-detected — it must be passed explicitly // via --buildNumber. Auto-detection only applies to release-native where the binary // build number is the natural targeting key. - const versionInfoPromise: Promise = (command.appStoreVersion && command.buildNumber) + const versionInfoPromise: Promise = command.appStoreVersion ? Q({ appVersion: command.appStoreVersion, buildNumber: command.buildNumber }) : getReactNativeProjectVersionInfo(command, projectName).then((detected) => ({ - appVersion: command.appStoreVersion || detected.appVersion, + appVersion: detected.appVersion, buildNumber: command.buildNumber, })); diff --git a/script/utils/gradle-utils.ts b/script/utils/gradle-utils.ts new file mode 100644 index 0000000..bab4690 --- /dev/null +++ b/script/utils/gradle-utils.ts @@ -0,0 +1,164 @@ +// Detects the Android versionName/versionCode a CodePush release should target. +// Groovy DSL (build.gradle) is parsed statically; Kotlin DSL (build.gradle.kts) may +// compute values from arbitrary expressions, so it is evaluated by running the +// project's Gradle wrapper with an injected task that prints the resolved values. + +import * as childProcess from "child_process"; +import * as fs from "fs"; +import * as os from "os"; +import * as path from "path"; +import { promisify } from "util"; +import * as chalk from "chalk"; + +import { fileExists } from "./file-utils"; +import { isValidVersion } from "../react-native-utils"; + +const g2js = require("gradle-to-js/lib/parser"); +const properties = require("properties"); + +const exec = promisify(childProcess.exec); + +export interface AndroidVersionInfo { + appVersion?: string; + buildNumber?: string; +} + +/** Raw values from the build script — literals or references to Gradle properties. */ +interface GradleVersionFields { + versionName: string | null; + versionCode: string | number | null; +} + +const FALLBACK_HINT = "Pass the version explicitly with --targetBinaryVersion to skip Gradle detection."; + +// "revopush"-prefixed to avoid collisions with project tasks; written in Groovy, +// which Gradle accepts as an init script for projects of either DSL. +const PRINT_VERSION_TASK = "_revopushPrintVersion"; +const printVersionInitScript = (moduleName: string) => ` +allprojects { + afterEvaluate { proj -> + if (proj.name == '${moduleName}') { + task ${PRINT_VERSION_TASK} { + doLast { + def android = proj.extensions.findByName('android') + if (android == null) { + throw new GradleException("Project ':${moduleName}' does not apply the Android Gradle plugin (no 'android' extension found).") + } + println groovy.json.JsonOutput.toJson([ + versionName: android.defaultConfig.versionName, + versionCode: android.defaultConfig.versionCode?.toString() + ]) + } + } + } + } +} +`.trim(); + +/** @param gradleFile build script path or its directory; defaults to "android/app". */ +export async function getAndroidVersionInfo(gradleFile?: string | null): Promise { + const buildFile = resolveGradleBuildFile(gradleFile ?? path.join("android", "app")); + const { versionName, versionCode } = buildFile.endsWith(".kts") + ? await evaluateKotlinDslBuildFile(buildFile) + : await parseGroovyDslBuildFile(buildFile); + + const appVersion = resolveAppVersion(versionName, buildFile); + const buildNumber = resolveBuildNumber(versionCode); + if (!buildNumber) { + console.log(chalk.yellow( + `Warning: Unable to read "android.defaultConfig.versionCode" from "${buildFile}". ` + + `This is expected if it is set dynamically (e.g. on CI). Pass --buildNumber explicitly to include it in the release.` + )); + } + return { appVersion, buildNumber }; +} + +/** Locates the build script: the given file itself, or inside the given directory (Kotlin DSL preferred). */ +function resolveGradleBuildFile(gradleFile: string): string { + const candidates = [gradleFile, path.join(gradleFile, "build.gradle.kts"), path.join(gradleFile, "build.gradle")]; + const buildFile = candidates.find(fileExists); + if (!buildFile) { + throw new Error(`Unable to find gradle file "${gradleFile}".`); + } + return buildFile; +} + +async function parseGroovyDslBuildFile(buildFile: string): Promise { + const parsed: any = await g2js.parseFile(buildFile).catch(() => { + throw new Error(`Unable to parse the "${buildFile}" file. Please ensure it is a well-formed Gradle file.`); + }); + // g2js yields an array when the file contains multiple 'android' blocks. + const androidBlocks: any[] = Array.isArray(parsed.android) ? parsed.android : [parsed.android]; + const defaultConfig = androidBlocks.find((block) => block?.defaultConfig)?.defaultConfig; + if (!defaultConfig && /^\s*val\s+/m.test(fs.readFileSync(buildFile, "utf8"))) { + throw new Error( + `"${buildFile}" appears to contain Kotlin DSL syntax. Gradle determines the script language by file extension — ` + + `rename the file to "${path.basename(buildFile)}.kts" to make it a valid Kotlin DSL build script.` + ); + } + return { + // g2js keeps the Groovy quotes around string literals — strip them. + versionName: defaultConfig?.versionName?.replace(/"/g, "").trim() ?? null, + versionCode: defaultConfig?.versionCode ?? null, + }; +} + +async function evaluateKotlinDslBuildFile(buildFile: string): Promise { + // The build file lives in the application module folder (typically android/app); + // its parent is the Gradle project root, and the folder name is the module name. + const moduleName = path.basename(path.dirname(path.resolve(buildFile))); + const androidDir = path.resolve(buildFile, "..", ".."); + const gradlew = path.join(androidDir, process.platform === "win32" ? "gradlew.bat" : "gradlew"); + if (!fileExists(gradlew)) { + throw new Error(`No Gradle wrapper found at "${gradlew}", required to evaluate "${buildFile}". ${FALLBACK_HINT}`); + } + + const initScript = path.join(os.tmpdir(), `revopush-init-${process.pid}.gradle`); + + fs.writeFileSync(initScript, printVersionInitScript(moduleName), "utf8"); + try { + const { stdout } = await exec( + `"${gradlew}" --project-dir "${androidDir}" --init-script "${initScript}" -q :${moduleName}:${PRINT_VERSION_TASK}`, + { timeout: 120000 } + ); + // The task's JSON is the last line; configuration-phase output (plugin notices, printlns) may precede it. + return JSON.parse(stdout.trim().split(/\r?\n/).pop() ?? ""); + } catch (error) { + throw new Error(`Gradle failed while reading the version from "${buildFile}": ${error.message}\n${FALLBACK_HINT}`); + } finally { + fs.rmSync(initScript, { force: true }); + } +} + +function resolveAppVersion(versionName: string | null, buildFile: string): string | undefined { + if (!versionName) return undefined; + + // A value that isn't valid semver and doesn't start with a digit is a property reference. + const isPropertyRef = !isValidVersion(versionName) && !/^\d/.test(versionName); + const appVersion = isPropertyRef ? lookupGradleProperty(versionName.replace("project.", "")) : versionName; + + if (!appVersion || !isValidVersion(appVersion)) { + throw new Error( + `Unable to resolve a valid semver app version (e.g. 1.3.2) from "android.defaultConfig.versionName" in "${buildFile}" ` + + `(found: "${versionName}"). ${FALLBACK_HINT}` + ); + } + return appVersion; +} + +function resolveBuildNumber(versionCode: string | number | null): string | undefined { + const text = versionCode?.toString(); + if (!text) return undefined; + // A non-numeric value is a reference to a Gradle property (e.g. "project.versionCode"). + return (/^\d+$/.test(text) ? text : lookupGradleProperty(text.replace("project.", ""))) || undefined; +} + +function lookupGradleProperty(key: string): string | undefined { + const files = [path.join("android", "app", "gradle.properties"), path.join("android", "gradle.properties")]; + for (const file of files) { + if (!fileExists(file)) continue; + // The properties parser type-converts values (e.g. "2" becomes a number) — normalize back to string. + const value = properties.parse(fs.readFileSync(file, "utf8"))?.[key]?.toString(); + if (value) return value; + } +} From 81f7d0d9d546602cdcd1f1ecceafc458139a278f Mon Sep 17 00:00:00 2001 From: Revopush Date: Thu, 11 Jun 2026 19:47:13 +0300 Subject: [PATCH 03/18] 0.0.13 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b77b041..8bb2afc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@revopush/code-push-cli", - "version": "0.0.12", + "version": "0.0.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@revopush/code-push-cli", - "version": "0.0.12", + "version": "0.0.13", "dependencies": { "@devicefarmer/adbkit-apkreader": "^3.2.4", "aab-parser": "^1.0.1", diff --git a/package.json b/package.json index a349460..9b0aaa5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@revopush/code-push-cli", - "version": "0.0.12", + "version": "0.0.13", "description": "Management CLI for the CodePush service", "main": "./script/cli.js", "scripts": { From bb8c64b2706decf0b5644a918a1a827d63287db7 Mon Sep 17 00:00:00 2001 From: Revopush Date: Thu, 11 Jun 2026 19:59:09 +0300 Subject: [PATCH 04/18] fix IPA extraction --- script/utils/file-utils.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/script/utils/file-utils.ts b/script/utils/file-utils.ts index fe2e1f5..af120ba 100644 --- a/script/utils/file-utils.ts +++ b/script/utils/file-utils.ts @@ -69,10 +69,7 @@ export async function downloadBlob(url: string, folder: string, filename: string } export async function extractIPA(zipPath: string, extractTo: string) { - const extractStream = unzipper.Extract({ path: extractTo }); - await new Promise((resolve, reject) => { - fs.createReadStream(zipPath).pipe(extractStream).on("close", resolve).on("error", reject); - }); + await fs.createReadStream(zipPath).pipe(unzipper.Extract({ path: extractTo })).promise(); } export async function extractAPK(zipPath: string, extractTo: string) { From ece092a7c42e3a3e2cde32910040211452a88bd0 Mon Sep 17 00:00:00 2001 From: Revopush Date: Mon, 15 Jun 2026 09:41:26 +0300 Subject: [PATCH 05/18] 0.0.14 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8bb2afc..b6fc456 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@revopush/code-push-cli", - "version": "0.0.13", + "version": "0.0.14", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@revopush/code-push-cli", - "version": "0.0.13", + "version": "0.0.14", "dependencies": { "@devicefarmer/adbkit-apkreader": "^3.2.4", "aab-parser": "^1.0.1", diff --git a/package.json b/package.json index 9b0aaa5..2a4bcd0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@revopush/code-push-cli", - "version": "0.0.13", + "version": "0.0.14", "description": "Management CLI for the CodePush service", "main": "./script/cli.js", "scripts": { From 2b9f1a8dd1aae4823376e834a5ec84b7bfa790a6 Mon Sep 17 00:00:00 2001 From: Revopush Date: Mon, 15 Jun 2026 09:43:50 +0300 Subject: [PATCH 06/18] 0.0.13 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b6fc456..8bb2afc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@revopush/code-push-cli", - "version": "0.0.14", + "version": "0.0.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@revopush/code-push-cli", - "version": "0.0.14", + "version": "0.0.13", "dependencies": { "@devicefarmer/adbkit-apkreader": "^3.2.4", "aab-parser": "^1.0.1", diff --git a/package.json b/package.json index 2a4bcd0..9b0aaa5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@revopush/code-push-cli", - "version": "0.0.14", + "version": "0.0.13", "description": "Management CLI for the CodePush service", "main": "./script/cli.js", "scripts": { From 0ae52e0c5455ed89652addcb2fc082d4292f2541 Mon Sep 17 00:00:00 2001 From: Revopush Date: Mon, 6 Jul 2026 00:27:10 +0300 Subject: [PATCH 07/18] Fix npm audit vulnerabilities in package-lock.json (lockfile-only) Update transitive dependency versions within existing package.json semver ranges (npm audit fix --package-lock-only). Reduces audit findings 24 -> 8. No package.json or node_modules changes. REV-28 Co-Authored-By: Claude Opus 4.8 (1M context) --- package-lock.json | 616 ++++++++++++++++++++++------------------------ 1 file changed, 291 insertions(+), 325 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8bb2afc..4ef2863 100644 --- a/package-lock.json +++ b/package-lock.json @@ -672,10 +672,9 @@ "license": "BSD-3-Clause" }, "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "license": "BSD-3-Clause" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", + "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==" }, "node_modules/@sinclair/typebox": { "version": "0.25.24", @@ -1246,10 +1245,9 @@ } }, "node_modules/@xmldom/xmldom": { - "version": "0.8.11", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz", - "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==", - "license": "MIT", + "version": "0.8.13", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz", + "integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==", "engines": { "node": ">=10.0.0" } @@ -1322,9 +1320,9 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", @@ -1337,16 +1335,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1369,20 +1357,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -1463,19 +1437,6 @@ "node": ">=0.6" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -1483,29 +1444,58 @@ "license": "MIT" }, "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", + "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", "dev": true, "dependencies": { - "bytes": "3.1.2", + "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", "type-is": "~1.6.18", - "unpipe": "1.0.0" + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/body-parser/node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/bplist-creator": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz", @@ -1527,10 +1517,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1577,24 +1566,6 @@ "node": ">= 0.8" } }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -1608,6 +1579,21 @@ "node": ">= 0.4" } }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1646,41 +1632,18 @@ } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, - "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" } }, "node_modules/ci-info": { @@ -1887,22 +1850,6 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -1940,11 +1887,10 @@ } }, "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } @@ -2369,40 +2315,39 @@ } }, "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", "dev": true, - "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", + "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", - "qs": "6.13.0", + "qs": "~6.15.1", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", + "send": "~0.19.0", + "serve-static": "~1.16.2", "setprototypeof": "1.2.0", - "statuses": "2.0.1", + "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -2589,9 +2534,9 @@ } }, "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true }, "node_modules/foreground-child": { @@ -2612,16 +2557,15 @@ } }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", - "license": "MIT", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -2679,21 +2623,6 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -2881,17 +2810,6 @@ "node": ">=8" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -2920,9 +2838,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "dependencies": { "function-bind": "^1.1.2" }, @@ -3039,19 +2957,6 @@ "node": ">= 0.10" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-core-module": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", @@ -3258,10 +3163,20 @@ "dev": true }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "dependencies": { "argparse": "^2.0.1" }, @@ -3338,21 +3253,21 @@ "dev": true }, "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", "dependencies": { - "buffer-equal-constant-time": "1.0.1", + "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", + "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", "dependencies": { - "jwa": "^1.4.1", + "jwa": "^1.4.2", "safe-buffer": "^5.0.1" } }, @@ -3394,9 +3309,9 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==" }, "node_modules/lodash.get": { "version": "4.4.2", @@ -3570,9 +3485,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -3614,29 +3529,29 @@ } }, "node_modules/mocha": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz", - "integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==", + "version": "11.7.6", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.6.tgz", + "integrity": "sha512-nS9xOGbw2I3cjCpxwZAEJ9xK9lmJ08vEkQvLtz4du9ZrF9UrjRpeJGiIgl2Z+Qs++pmB4ecDe48Fwsh+j+j7xA==", "dev": true, - "license": "MIT", "dependencies": { - "ansi-colors": "^4.1.3", "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", + "chokidar": "^4.0.1", "debug": "^4.3.5", - "diff": "^5.2.0", + "diff": "^7.0.0", "escape-string-regexp": "^4.0.0", "find-up": "^5.0.0", "glob": "^10.4.5", "he": "^1.2.0", + "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", + "minimatch": "^9.0.5", "ms": "^2.1.3", + "picocolors": "^1.1.1", "serialize-javascript": "^6.0.2", "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", - "workerpool": "^6.5.1", + "workerpool": "^9.2.0", "yargs": "^17.7.2", "yargs-parser": "^21.1.1", "yargs-unparser": "^2.0.0" @@ -3650,11 +3565,10 @@ } }, "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -3677,6 +3591,15 @@ } } }, + "node_modules/mocha/node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -3691,11 +3614,11 @@ } }, "node_modules/mocha/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, - "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -3711,14 +3634,13 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha/node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/mocha/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -3727,19 +3649,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -3840,20 +3749,10 @@ "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "license": "MIT" }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "engines": { "node": ">= 0.4" }, @@ -4024,11 +3923,10 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "dev": true, - "license": "MIT" + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "dev": true }, "node_modules/path-type": { "version": "4.0.0", @@ -4045,10 +3943,16 @@ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "license": "MIT" }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "engines": { "node": ">=8.6" @@ -4159,11 +4063,10 @@ } }, "node_modules/protobufjs": { - "version": "6.11.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz", - "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==", + "version": "6.11.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.6.tgz", + "integrity": "sha512-k8BHqgPBOtrlougZZqF2uUk5Z7bN8f0wj+3e8M3hvtSv0NBAz4VBy5f6R5Nxq/l+i7mRFTgNZb2trxqTpHNY/A==", "hasInstallScript": true, - "license": "BSD-3-Clause", "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -4216,11 +4119,12 @@ } }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", "dependencies": { - "side-channel": "^1.0.6" + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" }, "engines": { "node": ">=0.6" @@ -4269,16 +4173,45 @@ } }, "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", "dev": true, "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, "engines": { "node": ">= 0.8" } @@ -4328,16 +4261,16 @@ "license": "MIT" }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, "engines": { - "node": ">=8.10.0" + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/rechoir": { @@ -4548,22 +4481,6 @@ "node": ">= 0.8.0" } }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -4614,14 +4531,65 @@ } }, "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -5031,11 +4999,10 @@ } }, "node_modules/ts-node/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } @@ -5223,11 +5190,10 @@ "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" }, "node_modules/workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", - "dev": true, - "license": "Apache-2.0" + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", + "dev": true }, "node_modules/wrap-ansi": { "version": "7.0.0", From ba0c5d949117f22236cfec4ee3dc3aebb186442f Mon Sep 17 00:00:00 2001 From: Revopush Date: Mon, 6 Jul 2026 20:12:16 +0300 Subject: [PATCH 08/18] Replace unmaintained aab-parser to clear protobufjs CVEs (REV-36) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit protobufjs entered the tree only via aab-parser@1.0.1, which hard-pins protobufjs ^6.11.2. Every reachable 6.x version is affected by 11 npm audit advisories (worst: critical 9.8 RCE), and all fixes land above the pinned range, so npm audit fix could not resolve it. An npm `overrides` bump was insufficient: overrides are honored only for the root project (so end-users installing the published CLI stay vulnerable), and protobufjs 7.x's strict import resolution exposed that aab-parser ships a broken Resources.proto (imports an unshipped Configuration.proto), breaking AAB parsing at runtime. No maintained pure-JS alternative exists — the maintained options wrap Google's Java bundletool and would add a JRE requirement. Instead, vendor the small piece actually used: - Remove aab-parser; add maintained protobufjs ^7.6.3 and jszip ^3.10.1 as direct deps. - script/utils/aab-utils.ts: parseAabManifest() reads base/manifest/ AndroidManifest.xml (a protobuf-encoded aapt.pb.XmlNode). We only read a few attributes off the root element, so it declares just the minimal XmlNode -> XmlElement -> XmlAttribute slice (field numbers from AOSP's Resources.proto); the decoder skips every other field. Same return shape and error text as aab-parser. - Swap the single call site in command-executor.ts. Verified: tsc clean; protobufjs gone from npm audit; on a real 36MB app-release.aab the vendored parser (protobufjs 7.6.5) returns byte-identical metadata to the original aab-parser (protobufjs 6.11.x). Co-Authored-By: Claude Opus 4.8 (1M context) --- package-lock.json | 82 ++++++++++++-------------------------- package.json | 3 +- script/command-executor.ts | 4 +- script/utils/aab-utils.ts | 59 +++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 59 deletions(-) create mode 100644 script/utils/aab-utils.ts diff --git a/package-lock.json b/package-lock.json index 4ef2863..a6945c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,6 @@ "version": "0.0.13", "dependencies": { "@devicefarmer/adbkit-apkreader": "^3.2.4", - "aab-parser": "^1.0.1", "adm-zip": "^0.5.16", "backslash": "^0.2.0", "bplist-parser": "^0.3.2", @@ -19,6 +18,7 @@ "email-validator": "^2.0.4", "gradle-to-js": "2.0.1", "jsonwebtoken": "^9.0.2", + "jszip": "^3.10.1", "moment": "^2.29.4", "opener": "^1.5.2", "parse-duration": "1.1.0", @@ -26,6 +26,7 @@ "progress": "^2.0.3", "prompt": "^1.3.0", "properties": "^1.2.1", + "protobufjs": "^7.6.3", "q": "~1.5.1", "recursive-fs": "2.1.0", "rimraf": "^2.5.1", @@ -616,8 +617,7 @@ "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "license": "BSD-3-Clause" + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" }, "node_modules/@protobufjs/base64": { "version": "1.1.2", @@ -626,25 +626,21 @@ "license": "BSD-3-Clause" }, "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "license": "BSD-3-Clause" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==" }, "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "license": "BSD-3-Clause" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==" }, "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "license": "BSD-3-Clause", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" + "@protobufjs/aspromise": "^1.1.1" } }, "node_modules/@protobufjs/float": { @@ -653,12 +649,6 @@ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", "license": "BSD-3-Clause" }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "license": "BSD-3-Clause" - }, "node_modules/@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", @@ -844,12 +834,6 @@ "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", "dev": true }, - "node_modules/@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", - "license": "MIT" - }, "node_modules/@types/mime": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", @@ -1252,16 +1236,6 @@ "node": ">=10.0.0" } }, - "node_modules/aab-parser": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/aab-parser/-/aab-parser-1.0.1.tgz", - "integrity": "sha512-X8+gHK60IpKyCY454QLKW4fQ9u8rWPYNrE5j5FeDeruecBZxXmlgPNZEHE2Wg9hmXWiP+1HRIBwUQ/cflsSmUg==", - "license": "MIT", - "dependencies": { - "jszip": "^3.7.1", - "protobufjs": "^6.11.2" - } - }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -3377,10 +3351,9 @@ } }, "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", - "license": "Apache-2.0" + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==" }, "node_modules/lru-cache": { "version": "10.4.3", @@ -4063,28 +4036,25 @@ } }, "node_modules/protobufjs": { - "version": "6.11.6", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.6.tgz", - "integrity": "sha512-k8BHqgPBOtrlougZZqF2uUk5Z7bN8f0wj+3e8M3hvtSv0NBAz4VBy5f6R5Nxq/l+i7mRFTgNZb2trxqTpHNY/A==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz", + "integrity": "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", + "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", - "long": "^4.0.0" + "long": "^5.3.2" }, - "bin": { - "pbjs": "bin/pbjs", - "pbts": "bin/pbts" + "engines": { + "node": ">=12.0.0" } }, "node_modules/proxy-addr": { diff --git a/package.json b/package.json index 9b0aaa5..417d7ff 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ ], "dependencies": { "@devicefarmer/adbkit-apkreader": "^3.2.4", - "aab-parser": "^1.0.1", "adm-zip": "^0.5.16", "backslash": "^0.2.0", "bplist-parser": "^0.3.2", @@ -34,6 +33,7 @@ "email-validator": "^2.0.4", "gradle-to-js": "2.0.1", "jsonwebtoken": "^9.0.2", + "jszip": "^3.10.1", "moment": "^2.29.4", "opener": "^1.5.2", "parse-duration": "1.1.0", @@ -41,6 +41,7 @@ "progress": "^2.0.3", "prompt": "^1.3.0", "properties": "^1.2.1", + "protobufjs": "^7.6.3", "q": "~1.5.1", "recursive-fs": "2.1.0", "rimraf": "^2.5.1", diff --git a/script/command-executor.ts b/script/command-executor.ts index 0488e2d..4d43a52 100644 --- a/script/command-executor.ts +++ b/script/command-executor.ts @@ -15,7 +15,7 @@ import * as semver from "semver"; import * as cli from "../script/types/cli"; import sign from "./sign"; const ApkReader = require("@devicefarmer/adbkit-apkreader"); -const aabParser = require("aab-parser"); +import { parseAabManifest } from "./utils/aab-utils"; import { AccessKey, Account, @@ -1564,7 +1564,7 @@ export const releaseNative = (command: cli.IReleaseNativeCommand): Promise } else if (targetBinaryPathNormalised.endsWith(".aab")) { log(chalk.cyan(`\nExtracting AAB file:\n`)); await extractAAB(targetBinaryPath, extractFolder); - const { versionName: appStoreVersion, versionCode } = await aabParser.parseAabManifest(targetBinaryPath); + const { versionName: appStoreVersion, versionCode } = await parseAabManifest(targetBinaryPath); const metadataZip = await extractMetadataFromAndroid(`${extractFolder}/base`, outputFolder); // base folder is nested in AAB releaseCommandPartial = { diff --git a/script/utils/aab-utils.ts b/script/utils/aab-utils.ts new file mode 100644 index 0000000..2fe4203 --- /dev/null +++ b/script/utils/aab-utils.ts @@ -0,0 +1,59 @@ +// Minimal Android App Bundle (.aab) manifest reader; replaces the unmaintained +// aab-parser, which pinned a vulnerable protobufjs (^6.11.2). + +import * as fs from "fs"; +import * as jszip from "jszip"; +import * as protobuf from "protobufjs"; + +export type AabManifest = { + versionCode: number; + versionName: string; + packageName: string; + compiledSdkVersion: number; + compiledSdkVersionCodename: number; +}; + +type ManifestAttribute = { name: string; value: string }; + +// An AAB's is protobuf-encoded as an aapt.pb.XmlNode. We only read a +// few attributes, so we declare just that slice (field numbers from AOSP +// aapt2/Resources.proto); the decoder skips every field we omit. +const XmlNode = protobuf.parse(` + syntax = "proto3"; + package aapt.pb; + message XmlAttribute { string name = 2; string value = 3; } + message XmlElement { string name = 3; repeated XmlAttribute attribute = 4; } + message XmlNode { XmlElement element = 1; } +`).root.lookupType("aapt.pb.XmlNode"); + +async function readManifestAttributes(file: string | Buffer): Promise { + const buffer = typeof file === "string" ? await fs.promises.readFile(file) : file; + const archive = await jszip.loadAsync(buffer); + const manifest = await archive.file("base/manifest/AndroidManifest.xml")?.async("nodebuffer"); + if (manifest === undefined) { + throw new Error("Could not find AndroidManifest.xml file inside the app bundle file"); + } + + const decoded = XmlNode.decode(manifest).toJSON() as { element?: { attribute?: ManifestAttribute[] } }; + return decoded.element?.attribute ?? []; +} + +export async function parseAabManifest(file: string | Buffer): Promise { + const attributes = await readManifestAttributes(file); + + function getAttribute(name: string): string { + const attribute = attributes.find((attr) => attr.name === name); + if (attribute === undefined) { + throw new Error(`Attribute "${name}" not found in AndroidManifest.xml`); + } + return attribute.value; + } + + return { + versionCode: Number(getAttribute("versionCode")), + versionName: getAttribute("versionName"), + packageName: getAttribute("package"), + compiledSdkVersion: Number(getAttribute("compileSdkVersion")), + compiledSdkVersionCodename: Number(getAttribute("compileSdkVersionCodename")), + }; +} From 91cd48deac1bd70fa403b6e81068e7cde143453d Mon Sep 17 00:00:00 2001 From: Revopush Date: Tue, 7 Jul 2026 14:39:44 +0300 Subject: [PATCH 09/18] Fix parse-duration, serialize-javascript, and uuid CVEs (REV-43) Clears three Dependabot advisories in the CLI: - parse-duration (CVE-2025-25283, ReDoS): bump 1.1.0 -> ^2.1.6. v2 is ESM-only, so require() yields the module namespace and the parser is `.default`; it returns null (not NaN) for unparseable input, and `?? 0` preserves the pre-v2 behaviour of treating that as 0. Verified byte-identical to v1 across 18 TTL inputs (compound, decimal, empty, garbage). Adds engines: node >=20.19.0, required for require(esm). - serialize-javascript (GHSA-5c6j-r48x-rmvq, RCE/DoS): overridden to ^7.0.5. Transitive via mocha (devDependency), never shipped to consumers; no 6.x patch exists. - uuid (CVE-2026-41907, ReDoS): overridden to ^11.1.1. Transitive via xcode, which has no fixed release; verified compatible with xcode's uuid.v4() usage. Verified: tsc clean; CLI boots (require(esm) OK); xcode.generateUuid() works; npm audit clear for all three. Co-Authored-By: Claude Opus 4.8 (1M context) --- package-lock.json | 44 ++++++++++++++++++---------------------- package.json | 9 +++++++- script/command-parser.ts | 4 +++- 3 files changed, 31 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index a6945c5..e7a7c27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "jszip": "^3.10.1", "moment": "^2.29.4", "opener": "^1.5.2", - "parse-duration": "1.1.0", + "parse-duration": "^2.1.6", "plist": "^3.1.0", "progress": "^2.0.3", "prompt": "^1.3.0", @@ -65,6 +65,9 @@ "ts-node": "^10.9.2", "typescript": "^5.1.3", "which": "^3.0.1" + }, + "engines": { + "node": ">=20.19.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -3834,9 +3837,9 @@ } }, "node_modules/parse-duration": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/parse-duration/-/parse-duration-1.1.0.tgz", - "integrity": "sha512-z6t9dvSJYaPoQq7quMzdEagSFtpGu+utzHqqxmpVWNNZRIXnvqyCvn9XsTdh7c/w0Bqmdz3RB3YnRaKtpRtEXQ==" + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/parse-duration/-/parse-duration-2.1.6.tgz", + "integrity": "sha512-1/A2Exg3NcJGcYdgV/dn4frR7vO2hOW/ohQ4KIgbT4W3raVcpYSszPWiL6I6cKufi4jQM5NbGRXLBj8AoLM4iQ==" }, "node_modules/parseurl": { "version": "1.3.3", @@ -4123,16 +4126,6 @@ } ] }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -4427,13 +4420,12 @@ "dev": true }, "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.7.tgz", + "integrity": "sha512-YAy8Od6KV+uuwUuU50np8fGB/Aues6Y0nAhA9y/hId74PlKUcme4pXcBD46NWKr1Q4osN/iseZ17YqO1XfmI8g==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" + "engines": { + "node": ">=20.0.0" } }, "node_modules/serve-static": { @@ -5092,11 +5084,15 @@ } }, "node_modules/uuid": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", - "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", + "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist/esm/bin/uuid" } }, "node_modules/v8-compile-cache-lib": { diff --git a/package.json b/package.json index 417d7ff..19cd23b 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "jszip": "^3.10.1", "moment": "^2.29.4", "opener": "^1.5.2", - "parse-duration": "1.1.0", + "parse-duration": "^2.1.6", "plist": "^3.1.0", "progress": "^2.0.3", "prompt": "^1.3.0", @@ -57,6 +57,13 @@ "yargs": "^17.7.2", "yazl": "^2.5.1" }, + "overrides": { + "serialize-javascript": "^7.0.5", + "uuid": "^11.1.1" + }, + "engines": { + "node": ">=20.19.0" + }, "devDependencies": { "@types/express": "^4.17.17", "@types/jest": "^29.5.14", diff --git a/script/command-parser.ts b/script/command-parser.ts index fe1334e..1989e39 100644 --- a/script/command-parser.ts +++ b/script/command-parser.ts @@ -1673,5 +1673,7 @@ function isDefined(object: any): boolean { } function parseDurationMilliseconds(durationString: string): number { - return Math.floor(parseDuration(durationString)); + // parse-duration v2 is ESM: the parser is `.default`, and `?? 0` restores v1's + // handling of unparseable input (v2 returns null instead of 0). + return Math.floor(parseDuration.default(durationString) ?? 0); } From 94ba3ed34af2aa101761a42c9a511a99882184e9 Mon Sep 17 00:00:00 2001 From: Revopush Date: Sat, 18 Jul 2026 14:33:59 +0300 Subject: [PATCH 10/18] Bump adm-zip from 0.5.16 to 0.6.0 to fix DoS CVE-2026-39244 (REV-54) adm-zip < 0.6.0 allocates Buffer.alloc(declared_uncompressed_size) before CRC validation with no bounds check, so a ~120-byte crafted ZIP declaring ~4GB uncompressed can exhaust memory and crash the process. 0.6.0 bounds allocation to the actual data present. Used only by extractAPK/extractAAB in script/utils/file-utils.ts via extractAllTo, which is unaffected by 0.6.0's extractEntryTo behavior change. Verified: tsc build clean, npm audit clears adm-zip, and a real ~50MB app-release.apk extracts identically (966 entries -> 922 files). Co-Authored-By: Claude Opus 4.8 (1M context) --- package-lock.json | 11 +++++------ package.json | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index e7a7c27..c97390c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.13", "dependencies": { "@devicefarmer/adbkit-apkreader": "^3.2.4", - "adm-zip": "^0.5.16", + "adm-zip": "^0.6.0", "backslash": "^0.2.0", "bplist-parser": "^0.3.2", "chalk": "^4.1.2", @@ -1288,12 +1288,11 @@ } }, "node_modules/adm-zip": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", - "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", - "license": "MIT", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.6.0.tgz", + "integrity": "sha512-XleryMhbuksdKtofnWZ9Sk+4CUTbms4Mb/EU32SZwToAyZ5RgVos/ki8n+yr0LWHOGKuakbXTuuYNHLQjhddgg==", "engines": { - "node": ">=12.0" + "node": ">=14.0" } }, "node_modules/ajv": { diff --git a/package.json b/package.json index 19cd23b..1c4be98 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ ], "dependencies": { "@devicefarmer/adbkit-apkreader": "^3.2.4", - "adm-zip": "^0.5.16", + "adm-zip": "^0.6.0", "backslash": "^0.2.0", "bplist-parser": "^0.3.2", "chalk": "^4.1.2", From f85e286f8ec6ae5d9de40cbf6948c6771dc18e1d Mon Sep 17 00:00:00 2001 From: Revopush Date: Sat, 18 Jul 2026 14:43:22 +0300 Subject: [PATCH 11/18] Add CI workflow with required "check" job for PRs against main The "check" job (npm ci + build) runs on every PR targeting main and is named to satisfy the org-wide required-status-check branch protection rule. Lint runs as a separate informational job (continue-on-error) because the repo currently has pre-existing eslint errors. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/CI.yaml | 49 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/CI.yaml diff --git a/.github/workflows/CI.yaml b/.github/workflows/CI.yaml new file mode 100644 index 0000000..28f90fe --- /dev/null +++ b/.github/workflows/CI.yaml @@ -0,0 +1,49 @@ +name: CI + +on: + pull_request: + branches: [main] + +jobs: + # Required status check for merging into main (org-wide branch protection + # matches on the job name "check"). + check: + name: check + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20.19.0 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build + + # Informational only — not required for merge (repo has pre-existing lint + # errors). Remove `continue-on-error` once lint is clean to make it enforcing. + lint: + name: lint + runs-on: ubuntu-latest + continue-on-error: true + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20.19.0 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Lint + run: npm run lint From e1220514bc8811a06efd88204c5d5db06fcc621a Mon Sep 17 00:00:00 2001 From: Revopush Date: Sat, 18 Jul 2026 14:52:45 +0300 Subject: [PATCH 12/18] Fix lint via typescript-eslint no-unused-vars; make lint CI-enforcing The base eslint no-unused-vars rule is not TS-aware and falsely flagged every CommandType enum member and the ReleaseHook function-type parameter names. Switch to @typescript-eslint/no-unused-vars (plugin already installed), which correctly treats enum members and type-position parameters as used, and add ^_ ignore patterns for intentionally-unused vars/args. Also fix the one genuine issue it left: != -> !== in debug.ts (eqeqeq), and drop continue-on-error from the lint CI job now that lint is clean. Co-Authored-By: Claude Opus 4.8 (1M context) --- .eslintrc.json | 10 +++++++++- .github/workflows/CI.yaml | 3 --- script/commands/debug.ts | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 46328ad..3d0b419 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -5,12 +5,20 @@ "sourceType": "module", "project": "./tsconfig.json" }, + "plugins": ["@typescript-eslint"], "extends": [ ], "rules": { "no-var": "error", "prefer-const": "error", - "no-unused-vars": "error", + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": [ + "error", + { + "argsIgnorePattern": "^_", + "varsIgnorePattern": "^_" + } + ], "eqeqeq": "error", "no-eval": "error" } diff --git a/.github/workflows/CI.yaml b/.github/workflows/CI.yaml index 28f90fe..6f24960 100644 --- a/.github/workflows/CI.yaml +++ b/.github/workflows/CI.yaml @@ -26,12 +26,9 @@ jobs: - name: Build run: npm run build - # Informational only — not required for merge (repo has pre-existing lint - # errors). Remove `continue-on-error` once lint is clean to make it enforcing. lint: name: lint runs-on: ubuntu-latest - continue-on-error: true steps: - name: Checkout uses: actions/checkout@v4 diff --git a/script/commands/debug.ts b/script/commands/debug.ts index 0bcc8a1..787bf07 100644 --- a/script/commands/debug.ts +++ b/script/commands/debug.ts @@ -48,7 +48,7 @@ class AndroidDebugPlatform implements IDebugPlatform { private getNumberOfAvailableDevices(): number { const output = childProcess.execSync("adb devices").toString(); const matches = output.match(/\b(device)\b/gim); - if (matches != null) { + if (matches !== null) { return matches.length; } return 0; From 9fd89bbea355bbe3e782c8073cce985e2f313610 Mon Sep 17 00:00:00 2001 From: Revopush Date: Sat, 18 Jul 2026 14:52:45 +0300 Subject: [PATCH 13/18] Fix lint via typescript-eslint no-unused-vars; make lint CI-enforcing The base eslint no-unused-vars rule is not TS-aware and falsely flagged every CommandType enum member and the ReleaseHook function-type parameter names. Switch to @typescript-eslint/no-unused-vars (plugin already installed), which correctly treats enum members and type-position parameters as used, and add ^_ ignore patterns for intentionally-unused vars/args. Also fix the one genuine issue it left: != -> !== in debug.ts (eqeqeq), and drop continue-on-error from the lint CI job now that lint is clean. Co-Authored-By: Claude Opus 4.8 (1M context) --- .eslintrc.json | 10 +++++++++- .github/workflows/CI.yaml | 5 ----- script/commands/debug.ts | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 46328ad..3d0b419 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -5,12 +5,20 @@ "sourceType": "module", "project": "./tsconfig.json" }, + "plugins": ["@typescript-eslint"], "extends": [ ], "rules": { "no-var": "error", "prefer-const": "error", - "no-unused-vars": "error", + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": [ + "error", + { + "argsIgnorePattern": "^_", + "varsIgnorePattern": "^_" + } + ], "eqeqeq": "error", "no-eval": "error" } diff --git a/.github/workflows/CI.yaml b/.github/workflows/CI.yaml index 28f90fe..2906537 100644 --- a/.github/workflows/CI.yaml +++ b/.github/workflows/CI.yaml @@ -5,8 +5,6 @@ on: branches: [main] jobs: - # Required status check for merging into main (org-wide branch protection - # matches on the job name "check"). check: name: check runs-on: ubuntu-latest @@ -26,12 +24,9 @@ jobs: - name: Build run: npm run build - # Informational only — not required for merge (repo has pre-existing lint - # errors). Remove `continue-on-error` once lint is clean to make it enforcing. lint: name: lint runs-on: ubuntu-latest - continue-on-error: true steps: - name: Checkout uses: actions/checkout@v4 diff --git a/script/commands/debug.ts b/script/commands/debug.ts index 0bcc8a1..787bf07 100644 --- a/script/commands/debug.ts +++ b/script/commands/debug.ts @@ -48,7 +48,7 @@ class AndroidDebugPlatform implements IDebugPlatform { private getNumberOfAvailableDevices(): number { const output = childProcess.execSync("adb devices").toString(); const matches = output.match(/\b(device)\b/gim); - if (matches != null) { + if (matches !== null) { return matches.length; } return 0; From 87ef5bbe53048c3b23d00770a0db888d385cf45a Mon Sep 17 00:00:00 2001 From: Revopush Date: Sat, 18 Jul 2026 15:00:06 +0300 Subject: [PATCH 14/18] Harden CI workflow with GitHub Actions best practices - Pin actions to full commit SHAs (v7.0.0) instead of mutable tags, with version comments for readability/Dependabot - Add least-privilege top-level permissions (contents: read) - Add concurrency group with cancel-in-progress to drop superseded PR runs - Add timeout-minutes guard and persist-credentials: false on checkout Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/CI.yaml | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/.github/workflows/CI.yaml b/.github/workflows/CI.yaml index 2906537..6df18ec 100644 --- a/.github/workflows/CI.yaml +++ b/.github/workflows/CI.yaml @@ -4,16 +4,30 @@ on: pull_request: branches: [main] +# Least privilege: CI only reads the repository contents. +permissions: + contents: read + +# Cancel superseded runs when a PR is updated, to save CI minutes. +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: + # Required status check for merging into main (org-wide branch protection + # matches on the job name "check"). check: name: check runs-on: ubuntu-latest + timeout-minutes: 15 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 20.19.0 cache: npm @@ -27,12 +41,15 @@ jobs: lint: name: lint runs-on: ubuntu-latest + timeout-minutes: 15 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 20.19.0 cache: npm From 050c74174381f3744dbff7d6214de0888842a114 Mon Sep 17 00:00:00 2001 From: Revopush Date: Sat, 18 Jul 2026 15:01:18 +0300 Subject: [PATCH 15/18] Add Dependabot config for GitHub Actions updates Weekly version updates for the SHA-pinned actions in .github/workflows, grouped into a single PR. Dependabot bumps the commit SHA and the accompanying version comment. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/dependabot.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..63938cb --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,18 @@ +version: 2 +updates: + # Keep GitHub Actions (SHA-pinned in .github/workflows) up to date. + # Dependabot bumps the pinned commit SHA and updates the `# vX.Y.Z` comment. + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "ci" + labels: + - "dependencies" + - "github-actions" + # Collapse all action bumps into a single PR to reduce noise. + groups: + github-actions: + patterns: + - "*" From 26fb00514da3c590069a50156e12a8e545fd5606 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 15:34:11 +0000 Subject: [PATCH 16/18] ci: bump actions/checkout in the github-actions group Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 7.0.0 to 7.0.1 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0...3d3c42e5aac5ba805825da76410c181273ba90b1) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 7.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/CI.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yaml b/.github/workflows/CI.yaml index 6df18ec..7cd003d 100644 --- a/.github/workflows/CI.yaml +++ b/.github/workflows/CI.yaml @@ -22,7 +22,7 @@ jobs: timeout-minutes: 15 steps: - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false @@ -44,7 +44,7 @@ jobs: timeout-minutes: 15 steps: - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false From 56b9dcc207efcb834e5aa58c89e351a5052057db Mon Sep 17 00:00:00 2001 From: Revopush Date: Wed, 29 Jul 2026 21:25:36 +0300 Subject: [PATCH 17/18] Fix brace-expansion DoS advisories (REV-72) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clears two high-severity Dependabot advisories on brace-expansion: - GHSA-3jxr-9vmj-r5cp: DoS via exponential-time expansion of consecutive non-expanding {} groups (patched in 1.1.16 / 2.1.2). - GHSA-mh99-v99m-4gvg (CVE-2026-14257): DoS via unbounded expansion length causing an OOM crash; patched in 5.0.8 and backported to 1.1.17 / 2.1.3. Both installed copies were stale relative to their own semver ranges, so `npm update brace-expansion` was sufficient — lockfile-only, no override entry and no manifest change: - rimraf@2 -> glob@7 -> minimatch@3 (also eslint): 1.1.15 -> 1.1.17. Reachable from the production tree. - mocha@11 -> minimatch@9: 2.1.1 -> 2.1.3. Dev-only. Deliberately not overridden to 5.0.8: brace-expansion 5.x exports a named `expand` (exports.expand = expand) while minimatch does `const expand = require('brace-expansion')`, so forcing 5.x would break glob/rimraf/eslint/mocha at runtime. The maintenance backports are the fix. Verified against the installed copies: normal expansion unchanged (a{b,c}d{1..3} -> abd1,abd2,abd3,acd1,acd2,acd3 on both); 40 consecutive empty {} groups expand in 0ms; 6 chained 200KB groups now cap at 3.6M chars via the new EXPANSION_MAX_LENGTH=4000000 guard instead of growing unbounded. tsc --noEmit clean; eslint and mocha still resolve their globs. Note: npm audit and Dependabot will keep flagging GHSA-mh99-v99m-4gvg until GitHub amends it — the advisory declares one flat range `<= 5.0.7` with first_patched_version 5.0.8 and has not yet been updated for the 1.1.17 / 2.1.3 backports published 2026-07-28/29. The installed code is patched. Co-Authored-By: Claude Opus 5 (1M context) --- package-lock.json | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index c97390c..3cccca6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1493,9 +1493,10 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.17.tgz", + "integrity": "sha512-w+aeW/mkgM4PyRMOJCgi3fOrTm5Q8QY1OSfn2TO2iuDj3ezIHqejmuxbjfPrqUkgqRew1iqkyAn0tr0ZwHD9+w==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3540,10 +3541,11 @@ } }, "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.3.tgz", + "integrity": "sha512-DRdx5neNsG/QXbniLFWi2YmC/68oeOOmKz6zOjVk6ZS1ZLXgLIKqVEc6hWsmkjBbgii0SwaBTcJ5XKj5gzY/4A==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } From 3699262f8760528adee56c781e515f0ad42a1ee1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 19:45:58 +0000 Subject: [PATCH 18/18] ci: bump js-yaml in the npm_and_yarn group across 1 directory Bumps the npm_and_yarn group with 1 update in the / directory: [js-yaml](https://github.com/nodeca/js-yaml). Updates `js-yaml` from 4.3.0 to 4.3.1 - [Changelog](https://github.com/nodeca/js-yaml/blob/4.3.1/CHANGELOG.md) - [Commits](https://github.com/nodeca/js-yaml/compare/4.3.0...4.3.1) --- updated-dependencies: - dependency-name: js-yaml dependency-version: 4.3.1 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- package-lock.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3cccca6..a99e6c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3140,9 +3140,9 @@ "dev": true }, "node_modules/js-yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", - "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "dev": true, "funding": [ { @@ -3154,6 +3154,7 @@ "url": "https://github.com/sponsors/nodeca" } ], + "license": "MIT", "dependencies": { "argparse": "^2.0.1" },