@gpc-cli/core 0.9.73 → 0.9.74
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +132 -96
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5132,7 +5132,8 @@ async function loadPreflightConfig(configPath) {
|
|
|
5132
5132
|
}
|
|
5133
5133
|
|
|
5134
5134
|
// src/preflight/aab-reader.ts
|
|
5135
|
-
import {
|
|
5135
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
5136
|
+
import { fromBuffer } from "yauzl";
|
|
5136
5137
|
|
|
5137
5138
|
// src/preflight/manifest-parser.ts
|
|
5138
5139
|
import protobuf from "protobufjs";
|
|
@@ -5321,12 +5322,20 @@ var AAB_MANIFEST_PATH = "base/manifest/AndroidManifest.xml";
|
|
|
5321
5322
|
var APK_MANIFEST_PATH = "AndroidManifest.xml";
|
|
5322
5323
|
var SO_HEADER_BYTES = 4096;
|
|
5323
5324
|
var SO_PATH_RE = /(?:^|\/)lib\/[^/]+\/[^/]+\.so$/;
|
|
5325
|
+
var SCAN_TIMEOUT_MS = 6e4;
|
|
5324
5326
|
function detectManifestPath(filePath) {
|
|
5325
5327
|
return filePath.toLowerCase().endsWith(".apk") ? APK_MANIFEST_PATH : AAB_MANIFEST_PATH;
|
|
5326
5328
|
}
|
|
5327
5329
|
async function readAab(aabPath) {
|
|
5328
5330
|
const manifestPath = detectManifestPath(aabPath);
|
|
5329
|
-
|
|
5331
|
+
let buffer;
|
|
5332
|
+
try {
|
|
5333
|
+
buffer = await readFile10(aabPath);
|
|
5334
|
+
} catch (err) {
|
|
5335
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5336
|
+
throw new Error(`Could not read ${aabPath}: ${msg}`);
|
|
5337
|
+
}
|
|
5338
|
+
const { zipfile, entries, manifestBuf, soHeaders } = await openAndScan(buffer, manifestPath);
|
|
5330
5339
|
zipfile.close();
|
|
5331
5340
|
if (!manifestBuf) {
|
|
5332
5341
|
const fileType = aabPath.toLowerCase().endsWith(".apk") ? "APK" : "AAB";
|
|
@@ -5363,98 +5372,125 @@ function createFallbackManifest() {
|
|
|
5363
5372
|
providers: []
|
|
5364
5373
|
};
|
|
5365
5374
|
}
|
|
5366
|
-
function openAndScan(
|
|
5375
|
+
function openAndScan(buffer, manifestPath = AAB_MANIFEST_PATH) {
|
|
5367
5376
|
return new Promise((resolve2, reject) => {
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5377
|
+
let settled = false;
|
|
5378
|
+
const timer = setTimeout(() => {
|
|
5379
|
+
if (!settled) {
|
|
5380
|
+
settled = true;
|
|
5381
|
+
reject(new Error("Timed out reading the archive. The file may be corrupt or truncated."));
|
|
5372
5382
|
}
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5383
|
+
}, SCAN_TIMEOUT_MS);
|
|
5384
|
+
function settleResolve(value) {
|
|
5385
|
+
if (settled) return;
|
|
5386
|
+
settled = true;
|
|
5387
|
+
clearTimeout(timer);
|
|
5388
|
+
resolve2(value);
|
|
5389
|
+
}
|
|
5390
|
+
function settleReject(error) {
|
|
5391
|
+
if (settled) return;
|
|
5392
|
+
settled = true;
|
|
5393
|
+
clearTimeout(timer);
|
|
5394
|
+
reject(error);
|
|
5395
|
+
}
|
|
5396
|
+
try {
|
|
5397
|
+
fromBuffer(buffer, { lazyEntries: true }, (err, zipfile) => {
|
|
5398
|
+
if (err || !zipfile) {
|
|
5399
|
+
settleReject(err ?? new Error("Failed to open AAB file"));
|
|
5400
|
+
return;
|
|
5384
5401
|
}
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5402
|
+
const entries = [];
|
|
5403
|
+
const soHeaders = /* @__PURE__ */ new Map();
|
|
5404
|
+
let manifestBuf = null;
|
|
5405
|
+
let pendingStreams = 0;
|
|
5406
|
+
let entrysDone = false;
|
|
5407
|
+
function fail(error) {
|
|
5408
|
+
if (settled) return;
|
|
5409
|
+
try {
|
|
5410
|
+
zipfile.close();
|
|
5411
|
+
} catch {
|
|
5412
|
+
}
|
|
5413
|
+
settleReject(error);
|
|
5389
5414
|
}
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
zipfile.openReadStream(entry, (streamErr, stream) => {
|
|
5394
|
-
if (streamErr || !stream) {
|
|
5395
|
-
fail(streamErr ?? new Error(`Failed to read ${entry.fileName}`));
|
|
5396
|
-
return;
|
|
5415
|
+
function maybeResolve() {
|
|
5416
|
+
if (!settled && entrysDone && pendingStreams === 0) {
|
|
5417
|
+
settleResolve({ zipfile, entries, manifestBuf, soHeaders });
|
|
5397
5418
|
}
|
|
5398
|
-
const chunks = [];
|
|
5399
|
-
let totalBytes = 0;
|
|
5400
|
-
let done = false;
|
|
5401
|
-
stream.on("data", (chunk) => {
|
|
5402
|
-
chunks.push(chunk);
|
|
5403
|
-
totalBytes += chunk.length;
|
|
5404
|
-
if (maxBytes && totalBytes >= maxBytes) {
|
|
5405
|
-
stream.destroy();
|
|
5406
|
-
}
|
|
5407
|
-
});
|
|
5408
|
-
stream.on("error", (e) => {
|
|
5409
|
-
if (maxBytes && totalBytes >= maxBytes) return;
|
|
5410
|
-
fail(e);
|
|
5411
|
-
});
|
|
5412
|
-
const finish = () => {
|
|
5413
|
-
if (done) return;
|
|
5414
|
-
done = true;
|
|
5415
|
-
const buf = Buffer.concat(chunks);
|
|
5416
|
-
onData(maxBytes ? buf.subarray(0, maxBytes) : buf);
|
|
5417
|
-
pendingStreams--;
|
|
5418
|
-
maybeResolve();
|
|
5419
|
-
};
|
|
5420
|
-
stream.on("end", finish);
|
|
5421
|
-
stream.on("close", () => {
|
|
5422
|
-
if (totalBytes > 0 && maxBytes) finish();
|
|
5423
|
-
});
|
|
5424
|
-
});
|
|
5425
|
-
}
|
|
5426
|
-
zipfile.on("error", fail);
|
|
5427
|
-
zipfile.on("entry", (entry) => {
|
|
5428
|
-
if (rejected) return;
|
|
5429
|
-
const path = entry.fileName;
|
|
5430
|
-
if (!path.endsWith("/")) {
|
|
5431
|
-
entries.push({
|
|
5432
|
-
path,
|
|
5433
|
-
compressedSize: entry.compressedSize,
|
|
5434
|
-
uncompressedSize: entry.uncompressedSize
|
|
5435
|
-
});
|
|
5436
5419
|
}
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5420
|
+
function readEntryStream(entry, onData, maxBytes) {
|
|
5421
|
+
pendingStreams++;
|
|
5422
|
+
zipfile.openReadStream(entry, (streamErr, stream) => {
|
|
5423
|
+
if (streamErr || !stream) {
|
|
5424
|
+
fail(streamErr ?? new Error(`Failed to read ${entry.fileName}`));
|
|
5425
|
+
return;
|
|
5426
|
+
}
|
|
5427
|
+
const chunks = [];
|
|
5428
|
+
let totalBytes = 0;
|
|
5429
|
+
let done = false;
|
|
5430
|
+
stream.on("data", (chunk) => {
|
|
5431
|
+
chunks.push(chunk);
|
|
5432
|
+
totalBytes += chunk.length;
|
|
5433
|
+
if (maxBytes && totalBytes >= maxBytes) {
|
|
5434
|
+
stream.destroy();
|
|
5435
|
+
}
|
|
5436
|
+
});
|
|
5437
|
+
stream.on("error", (e) => {
|
|
5438
|
+
if (maxBytes && totalBytes >= maxBytes) return;
|
|
5439
|
+
fail(e);
|
|
5440
|
+
});
|
|
5441
|
+
const finish = () => {
|
|
5442
|
+
if (done) return;
|
|
5443
|
+
done = true;
|
|
5444
|
+
const buf = Buffer.concat(chunks);
|
|
5445
|
+
onData(maxBytes ? buf.subarray(0, maxBytes) : buf);
|
|
5446
|
+
pendingStreams--;
|
|
5447
|
+
maybeResolve();
|
|
5448
|
+
};
|
|
5449
|
+
stream.on("end", finish);
|
|
5450
|
+
stream.on("close", () => {
|
|
5451
|
+
if (totalBytes > 0 && maxBytes) finish();
|
|
5452
|
+
});
|
|
5440
5453
|
});
|
|
5441
|
-
} else if (SO_PATH_RE.test(path)) {
|
|
5442
|
-
readEntryStream(
|
|
5443
|
-
entry,
|
|
5444
|
-
(buf) => {
|
|
5445
|
-
soHeaders.set(path, buf);
|
|
5446
|
-
},
|
|
5447
|
-
SO_HEADER_BYTES
|
|
5448
|
-
);
|
|
5449
5454
|
}
|
|
5455
|
+
zipfile.on("error", fail);
|
|
5456
|
+
zipfile.on("entry", (entry) => {
|
|
5457
|
+
if (settled) return;
|
|
5458
|
+
try {
|
|
5459
|
+
const path = entry.fileName;
|
|
5460
|
+
if (!path.endsWith("/")) {
|
|
5461
|
+
entries.push({
|
|
5462
|
+
path,
|
|
5463
|
+
compressedSize: entry.compressedSize,
|
|
5464
|
+
uncompressedSize: entry.uncompressedSize
|
|
5465
|
+
});
|
|
5466
|
+
}
|
|
5467
|
+
if (path === manifestPath) {
|
|
5468
|
+
readEntryStream(entry, (buf) => {
|
|
5469
|
+
manifestBuf = buf;
|
|
5470
|
+
});
|
|
5471
|
+
} else if (SO_PATH_RE.test(path)) {
|
|
5472
|
+
readEntryStream(
|
|
5473
|
+
entry,
|
|
5474
|
+
(buf) => {
|
|
5475
|
+
soHeaders.set(path, buf);
|
|
5476
|
+
},
|
|
5477
|
+
SO_HEADER_BYTES
|
|
5478
|
+
);
|
|
5479
|
+
}
|
|
5480
|
+
zipfile.readEntry();
|
|
5481
|
+
} catch (e) {
|
|
5482
|
+
fail(e instanceof Error ? e : new Error(String(e)));
|
|
5483
|
+
}
|
|
5484
|
+
});
|
|
5485
|
+
zipfile.on("end", () => {
|
|
5486
|
+
entrysDone = true;
|
|
5487
|
+
maybeResolve();
|
|
5488
|
+
});
|
|
5450
5489
|
zipfile.readEntry();
|
|
5451
5490
|
});
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
});
|
|
5456
|
-
zipfile.readEntry();
|
|
5457
|
-
});
|
|
5491
|
+
} catch (err) {
|
|
5492
|
+
settleReject(err instanceof Error ? err : new Error(String(err)));
|
|
5493
|
+
}
|
|
5458
5494
|
});
|
|
5459
5495
|
}
|
|
5460
5496
|
|
|
@@ -6018,7 +6054,7 @@ ${fileList}` + (hasPageSizeCompat ? "\nandroid:pageSizeCompat is set, so the app
|
|
|
6018
6054
|
}
|
|
6019
6055
|
|
|
6020
6056
|
// src/preflight/scanners/metadata-scanner.ts
|
|
6021
|
-
import { readdir as readdir4, stat as stat6, readFile as
|
|
6057
|
+
import { readdir as readdir4, stat as stat6, readFile as readFile11 } from "fs/promises";
|
|
6022
6058
|
import { join as join8 } from "path";
|
|
6023
6059
|
var SAFE_LANG = /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$/;
|
|
6024
6060
|
var FILE_MAP2 = {
|
|
@@ -6077,7 +6113,7 @@ var metadataScanner = {
|
|
|
6077
6113
|
for (const [fileName, field] of Object.entries(FILE_MAP2)) {
|
|
6078
6114
|
const filePath = join8(langDir, fileName);
|
|
6079
6115
|
try {
|
|
6080
|
-
const content = await
|
|
6116
|
+
const content = await readFile11(filePath, "utf-8");
|
|
6081
6117
|
fields[field] = content.trimEnd();
|
|
6082
6118
|
} catch {
|
|
6083
6119
|
}
|
|
@@ -6150,7 +6186,7 @@ var metadataScanner = {
|
|
|
6150
6186
|
const defaultLang = locales.includes("en-US") ? "en-US" : locales[0];
|
|
6151
6187
|
const privacyPath = join8(dir, defaultLang, "privacy_policy_url.txt");
|
|
6152
6188
|
try {
|
|
6153
|
-
const url = await
|
|
6189
|
+
const url = await readFile11(privacyPath, "utf-8");
|
|
6154
6190
|
if (!url.trim()) throw new Error("empty");
|
|
6155
6191
|
} catch {
|
|
6156
6192
|
findings.push({
|
|
@@ -6175,7 +6211,7 @@ var metadataScanner = {
|
|
|
6175
6211
|
};
|
|
6176
6212
|
|
|
6177
6213
|
// src/preflight/scanners/secrets-scanner.ts
|
|
6178
|
-
import { readFile as
|
|
6214
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
6179
6215
|
|
|
6180
6216
|
// src/preflight/scan-files.ts
|
|
6181
6217
|
import { readdir as readdir5, stat as stat7 } from "fs/promises";
|
|
@@ -6295,7 +6331,7 @@ var secretsScanner = {
|
|
|
6295
6331
|
for (const filePath of files) {
|
|
6296
6332
|
let content;
|
|
6297
6333
|
try {
|
|
6298
|
-
content = await
|
|
6334
|
+
content = await readFile12(filePath, "utf-8");
|
|
6299
6335
|
} catch {
|
|
6300
6336
|
continue;
|
|
6301
6337
|
}
|
|
@@ -6323,7 +6359,7 @@ var secretsScanner = {
|
|
|
6323
6359
|
};
|
|
6324
6360
|
|
|
6325
6361
|
// src/preflight/scanners/billing-scanner.ts
|
|
6326
|
-
import { readFile as
|
|
6362
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
6327
6363
|
var BILLING_PATTERNS = [
|
|
6328
6364
|
{
|
|
6329
6365
|
ruleId: "billing-stripe-sdk",
|
|
@@ -6384,7 +6420,7 @@ var billingScanner = {
|
|
|
6384
6420
|
for (const filePath of files) {
|
|
6385
6421
|
let content;
|
|
6386
6422
|
try {
|
|
6387
|
-
content = await
|
|
6423
|
+
content = await readFile13(filePath, "utf-8");
|
|
6388
6424
|
} catch {
|
|
6389
6425
|
continue;
|
|
6390
6426
|
}
|
|
@@ -6410,7 +6446,7 @@ var billingScanner = {
|
|
|
6410
6446
|
};
|
|
6411
6447
|
|
|
6412
6448
|
// src/preflight/scanners/privacy-scanner.ts
|
|
6413
|
-
import { readFile as
|
|
6449
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
6414
6450
|
var TRACKING_SDKS = [
|
|
6415
6451
|
{
|
|
6416
6452
|
name: "Facebook SDK",
|
|
@@ -6450,7 +6486,7 @@ var privacyScanner = {
|
|
|
6450
6486
|
for (const filePath of files) {
|
|
6451
6487
|
let content;
|
|
6452
6488
|
try {
|
|
6453
|
-
content = await
|
|
6489
|
+
content = await readFile14(filePath, "utf-8");
|
|
6454
6490
|
} catch {
|
|
6455
6491
|
continue;
|
|
6456
6492
|
}
|
|
@@ -7150,7 +7186,7 @@ async function downloadGeneratedApk(client, packageName, versionCode, apkId, out
|
|
|
7150
7186
|
}
|
|
7151
7187
|
|
|
7152
7188
|
// src/commands/bundle-analysis.ts
|
|
7153
|
-
import { readFile as
|
|
7189
|
+
import { readFile as readFile15, stat as stat8 } from "fs/promises";
|
|
7154
7190
|
var EOCD_SIGNATURE = 101010256;
|
|
7155
7191
|
var CD_SIGNATURE = 33639248;
|
|
7156
7192
|
var MODULE_SUBDIRS = /* @__PURE__ */ new Set(["dex", "manifest", "res", "assets", "lib", "resources.pb", "root"]);
|
|
@@ -7230,7 +7266,7 @@ async function analyzeBundle(filePath) {
|
|
|
7230
7266
|
if (!fileInfo || !fileInfo.isFile()) {
|
|
7231
7267
|
throw new Error(`File not found: ${filePath}`);
|
|
7232
7268
|
}
|
|
7233
|
-
const buf = await
|
|
7269
|
+
const buf = await readFile15(filePath);
|
|
7234
7270
|
const cdEntries = parseCentralDirectory(buf);
|
|
7235
7271
|
const fileType = detectFileType2(filePath);
|
|
7236
7272
|
const isAab = fileType === "aab";
|
|
@@ -7316,7 +7352,7 @@ function topFiles(analysis, n = 20) {
|
|
|
7316
7352
|
async function checkBundleSize(analysis, configPath = ".bundlesize.json") {
|
|
7317
7353
|
let config;
|
|
7318
7354
|
try {
|
|
7319
|
-
const raw = await
|
|
7355
|
+
const raw = await readFile15(configPath, "utf-8");
|
|
7320
7356
|
config = JSON.parse(raw);
|
|
7321
7357
|
} catch {
|
|
7322
7358
|
return { passed: true, violations: [] };
|