Skip to content

Commit 31ba240

Browse files
authored
Merge pull request #37 from revopush/feature/rev-36
Replace unmaintained aab-parser to clear protobufjs CVEs (REV-36)
2 parents e06571d + ba0c5d9 commit 31ba240

4 files changed

Lines changed: 89 additions & 59 deletions

File tree

package-lock.json

Lines changed: 26 additions & 56 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
],
2525
"dependencies": {
2626
"@devicefarmer/adbkit-apkreader": "^3.2.4",
27-
"aab-parser": "^1.0.1",
2827
"adm-zip": "^0.5.16",
2928
"backslash": "^0.2.0",
3029
"bplist-parser": "^0.3.2",
@@ -34,13 +33,15 @@
3433
"email-validator": "^2.0.4",
3534
"gradle-to-js": "2.0.1",
3635
"jsonwebtoken": "^9.0.2",
36+
"jszip": "^3.10.1",
3737
"moment": "^2.29.4",
3838
"opener": "^1.5.2",
3939
"parse-duration": "1.1.0",
4040
"plist": "^3.1.0",
4141
"progress": "^2.0.3",
4242
"prompt": "^1.3.0",
4343
"properties": "^1.2.1",
44+
"protobufjs": "^7.6.3",
4445
"q": "~1.5.1",
4546
"recursive-fs": "2.1.0",
4647
"rimraf": "^2.5.1",

script/command-executor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import * as semver from "semver";
1515
import * as cli from "../script/types/cli";
1616
import sign from "./sign";
1717
const ApkReader = require("@devicefarmer/adbkit-apkreader");
18-
const aabParser = require("aab-parser");
18+
import { parseAabManifest } from "./utils/aab-utils";
1919
import {
2020
AccessKey,
2121
Account,
@@ -1564,7 +1564,7 @@ export const releaseNative = (command: cli.IReleaseNativeCommand): Promise<void>
15641564
} else if (targetBinaryPathNormalised.endsWith(".aab")) {
15651565
log(chalk.cyan(`\nExtracting AAB file:\n`));
15661566
await extractAAB(targetBinaryPath, extractFolder);
1567-
const { versionName: appStoreVersion, versionCode } = await aabParser.parseAabManifest(targetBinaryPath);
1567+
const { versionName: appStoreVersion, versionCode } = await parseAabManifest(targetBinaryPath);
15681568

15691569
const metadataZip = await extractMetadataFromAndroid(`${extractFolder}/base`, outputFolder); // base folder is nested in AAB
15701570
releaseCommandPartial = {

script/utils/aab-utils.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Minimal Android App Bundle (.aab) manifest reader; replaces the unmaintained
2+
// aab-parser, which pinned a vulnerable protobufjs (^6.11.2).
3+
4+
import * as fs from "fs";
5+
import * as jszip from "jszip";
6+
import * as protobuf from "protobufjs";
7+
8+
export type AabManifest = {
9+
versionCode: number;
10+
versionName: string;
11+
packageName: string;
12+
compiledSdkVersion: number;
13+
compiledSdkVersionCodename: number;
14+
};
15+
16+
type ManifestAttribute = { name: string; value: string };
17+
18+
// An AAB's <manifest> is protobuf-encoded as an aapt.pb.XmlNode. We only read a
19+
// few attributes, so we declare just that slice (field numbers from AOSP
20+
// aapt2/Resources.proto); the decoder skips every field we omit.
21+
const XmlNode = protobuf.parse(`
22+
syntax = "proto3";
23+
package aapt.pb;
24+
message XmlAttribute { string name = 2; string value = 3; }
25+
message XmlElement { string name = 3; repeated XmlAttribute attribute = 4; }
26+
message XmlNode { XmlElement element = 1; }
27+
`).root.lookupType("aapt.pb.XmlNode");
28+
29+
async function readManifestAttributes(file: string | Buffer): Promise<ManifestAttribute[]> {
30+
const buffer = typeof file === "string" ? await fs.promises.readFile(file) : file;
31+
const archive = await jszip.loadAsync(buffer);
32+
const manifest = await archive.file("base/manifest/AndroidManifest.xml")?.async("nodebuffer");
33+
if (manifest === undefined) {
34+
throw new Error("Could not find AndroidManifest.xml file inside the app bundle file");
35+
}
36+
37+
const decoded = XmlNode.decode(manifest).toJSON() as { element?: { attribute?: ManifestAttribute[] } };
38+
return decoded.element?.attribute ?? [];
39+
}
40+
41+
export async function parseAabManifest(file: string | Buffer): Promise<AabManifest> {
42+
const attributes = await readManifestAttributes(file);
43+
44+
function getAttribute(name: string): string {
45+
const attribute = attributes.find((attr) => attr.name === name);
46+
if (attribute === undefined) {
47+
throw new Error(`Attribute "${name}" not found in AndroidManifest.xml`);
48+
}
49+
return attribute.value;
50+
}
51+
52+
return {
53+
versionCode: Number(getAttribute("versionCode")),
54+
versionName: getAttribute("versionName"),
55+
packageName: getAttribute("package"),
56+
compiledSdkVersion: Number(getAttribute("compileSdkVersion")),
57+
compiledSdkVersionCodename: Number(getAttribute("compileSdkVersionCodename")),
58+
};
59+
}

0 commit comments

Comments
 (0)