Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 26 additions & 56 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -34,13 +33,15 @@
"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",
"plist": "^3.1.0",
"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",
Expand Down
4 changes: 2 additions & 2 deletions script/command-executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -1564,7 +1564,7 @@ export const releaseNative = (command: cli.IReleaseNativeCommand): Promise<void>
} 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 = {
Expand Down
59 changes: 59 additions & 0 deletions script/utils/aab-utils.ts
Original file line number Diff line number Diff line change
@@ -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 <manifest> 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<ManifestAttribute[]> {
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<AabManifest> {
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")),
};
}