@zhuixin/engine 0.2.3 → 0.2.5
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/README.md +10 -0
- package/dist/engine-main.js +1419 -163
- package/package.json +6 -4
package/dist/engine-main.js
CHANGED
|
@@ -363,25 +363,25 @@ var require_utils = __commonJS({
|
|
|
363
363
|
}
|
|
364
364
|
mkdirSync(folder);
|
|
365
365
|
};
|
|
366
|
-
Utils.prototype.writeFileTo = function(
|
|
366
|
+
Utils.prototype.writeFileTo = function(path10, content, overwrite, attr) {
|
|
367
367
|
const self = this;
|
|
368
|
-
if (self.fs.existsSync(
|
|
368
|
+
if (self.fs.existsSync(path10)) {
|
|
369
369
|
if (!overwrite) return false;
|
|
370
|
-
var stat = self.fs.statSync(
|
|
370
|
+
var stat = self.fs.statSync(path10);
|
|
371
371
|
if (stat.isDirectory()) {
|
|
372
372
|
return false;
|
|
373
373
|
}
|
|
374
374
|
}
|
|
375
|
-
var folder = pth.dirname(
|
|
375
|
+
var folder = pth.dirname(path10);
|
|
376
376
|
if (!self.fs.existsSync(folder)) {
|
|
377
377
|
self.makeDir(folder);
|
|
378
378
|
}
|
|
379
379
|
var fd;
|
|
380
380
|
try {
|
|
381
|
-
fd = self.fs.openSync(
|
|
381
|
+
fd = self.fs.openSync(path10, "w", 438);
|
|
382
382
|
} catch (e) {
|
|
383
|
-
self.fs.chmodSync(
|
|
384
|
-
fd = self.fs.openSync(
|
|
383
|
+
self.fs.chmodSync(path10, 438);
|
|
384
|
+
fd = self.fs.openSync(path10, "w", 438);
|
|
385
385
|
}
|
|
386
386
|
if (fd) {
|
|
387
387
|
try {
|
|
@@ -390,31 +390,31 @@ var require_utils = __commonJS({
|
|
|
390
390
|
self.fs.closeSync(fd);
|
|
391
391
|
}
|
|
392
392
|
}
|
|
393
|
-
self.fs.chmodSync(
|
|
393
|
+
self.fs.chmodSync(path10, attr || 438);
|
|
394
394
|
return true;
|
|
395
395
|
};
|
|
396
|
-
Utils.prototype.writeFileToAsync = function(
|
|
396
|
+
Utils.prototype.writeFileToAsync = function(path10, content, overwrite, attr, callback) {
|
|
397
397
|
if (typeof attr === "function") {
|
|
398
398
|
callback = attr;
|
|
399
399
|
attr = void 0;
|
|
400
400
|
}
|
|
401
401
|
const self = this;
|
|
402
|
-
self.fs.exists(
|
|
402
|
+
self.fs.exists(path10, function(exist) {
|
|
403
403
|
if (exist && !overwrite) return callback(false);
|
|
404
|
-
self.fs.stat(
|
|
404
|
+
self.fs.stat(path10, function(err, stat) {
|
|
405
405
|
if (exist && stat.isDirectory()) {
|
|
406
406
|
return callback(false);
|
|
407
407
|
}
|
|
408
|
-
var folder = pth.dirname(
|
|
408
|
+
var folder = pth.dirname(path10);
|
|
409
409
|
self.fs.exists(folder, function(exists) {
|
|
410
410
|
if (!exists) self.makeDir(folder);
|
|
411
|
-
self.fs.open(
|
|
411
|
+
self.fs.open(path10, "w", 438, function(err2, fd) {
|
|
412
412
|
if (err2) {
|
|
413
|
-
self.fs.chmod(
|
|
414
|
-
self.fs.open(
|
|
413
|
+
self.fs.chmod(path10, 438, function() {
|
|
414
|
+
self.fs.open(path10, "w", 438, function(err3, fd2) {
|
|
415
415
|
self.fs.write(fd2, content, 0, content.length, 0, function() {
|
|
416
416
|
self.fs.close(fd2, function() {
|
|
417
|
-
self.fs.chmod(
|
|
417
|
+
self.fs.chmod(path10, attr || 438, function() {
|
|
418
418
|
callback(true);
|
|
419
419
|
});
|
|
420
420
|
});
|
|
@@ -424,13 +424,13 @@ var require_utils = __commonJS({
|
|
|
424
424
|
} else if (fd) {
|
|
425
425
|
self.fs.write(fd, content, 0, content.length, 0, function() {
|
|
426
426
|
self.fs.close(fd, function() {
|
|
427
|
-
self.fs.chmod(
|
|
427
|
+
self.fs.chmod(path10, attr || 438, function() {
|
|
428
428
|
callback(true);
|
|
429
429
|
});
|
|
430
430
|
});
|
|
431
431
|
});
|
|
432
432
|
} else {
|
|
433
|
-
self.fs.chmod(
|
|
433
|
+
self.fs.chmod(path10, attr || 438, function() {
|
|
434
434
|
callback(true);
|
|
435
435
|
});
|
|
436
436
|
}
|
|
@@ -439,7 +439,7 @@ var require_utils = __commonJS({
|
|
|
439
439
|
});
|
|
440
440
|
});
|
|
441
441
|
};
|
|
442
|
-
Utils.prototype.findFiles = function(
|
|
442
|
+
Utils.prototype.findFiles = function(path10) {
|
|
443
443
|
const self = this;
|
|
444
444
|
function findSync(dir, pattern, recursive) {
|
|
445
445
|
if (typeof pattern === "boolean") {
|
|
@@ -448,16 +448,16 @@ var require_utils = __commonJS({
|
|
|
448
448
|
}
|
|
449
449
|
let files = [];
|
|
450
450
|
self.fs.readdirSync(dir).forEach(function(file) {
|
|
451
|
-
const
|
|
452
|
-
const stat = self.fs.statSync(
|
|
453
|
-
if (!pattern || pattern.test(
|
|
454
|
-
files.push(pth.normalize(
|
|
451
|
+
const path11 = pth.join(dir, file);
|
|
452
|
+
const stat = self.fs.statSync(path11);
|
|
453
|
+
if (!pattern || pattern.test(path11)) {
|
|
454
|
+
files.push(pth.normalize(path11) + (stat.isDirectory() ? self.sep : ""));
|
|
455
455
|
}
|
|
456
|
-
if (stat.isDirectory() && recursive) files = files.concat(findSync(
|
|
456
|
+
if (stat.isDirectory() && recursive) files = files.concat(findSync(path11, pattern, recursive));
|
|
457
457
|
});
|
|
458
458
|
return files;
|
|
459
459
|
}
|
|
460
|
-
return findSync(
|
|
460
|
+
return findSync(path10, void 0, true);
|
|
461
461
|
};
|
|
462
462
|
Utils.prototype.findFilesAsync = function(dir, cb) {
|
|
463
463
|
const self = this;
|
|
@@ -512,14 +512,14 @@ var require_utils = __commonJS({
|
|
|
512
512
|
return "UNSUPPORTED (" + method + ")";
|
|
513
513
|
}
|
|
514
514
|
};
|
|
515
|
-
Utils.canonical = function(
|
|
516
|
-
if (!
|
|
517
|
-
const safeSuffix = pth.posix.normalize("/" +
|
|
515
|
+
Utils.canonical = function(path10) {
|
|
516
|
+
if (!path10) return "";
|
|
517
|
+
const safeSuffix = pth.posix.normalize("/" + path10.split("\\").join("/"));
|
|
518
518
|
return pth.join(".", safeSuffix);
|
|
519
519
|
};
|
|
520
|
-
Utils.zipnamefix = function(
|
|
521
|
-
if (!
|
|
522
|
-
const safeSuffix = pth.posix.normalize("/" +
|
|
520
|
+
Utils.zipnamefix = function(path10) {
|
|
521
|
+
if (!path10) return "";
|
|
522
|
+
const safeSuffix = pth.posix.normalize("/" + path10.split("\\").join("/"));
|
|
523
523
|
return pth.posix.join(".", safeSuffix);
|
|
524
524
|
};
|
|
525
525
|
Utils.findLast = function(arr, callback) {
|
|
@@ -536,9 +536,9 @@ var require_utils = __commonJS({
|
|
|
536
536
|
prefix = pth.resolve(pth.normalize(prefix));
|
|
537
537
|
var parts = name.split("/");
|
|
538
538
|
for (var i = 0, l = parts.length; i < l; i++) {
|
|
539
|
-
var
|
|
540
|
-
if (
|
|
541
|
-
return
|
|
539
|
+
var path10 = pth.normalize(pth.join(prefix, parts.slice(i, l).join(pth.sep)));
|
|
540
|
+
if (path10 === prefix || path10.startsWith(prefix + pth.sep)) {
|
|
541
|
+
return path10;
|
|
542
542
|
}
|
|
543
543
|
}
|
|
544
544
|
return pth.normalize(pth.join(prefix, pth.basename(name)));
|
|
@@ -585,8 +585,8 @@ var require_fattr = __commonJS({
|
|
|
585
585
|
"node_modules/.pnpm/adm-zip@0.5.18/node_modules/adm-zip/util/fattr.js"(exports2, module2) {
|
|
586
586
|
"use strict";
|
|
587
587
|
var pth = require("path");
|
|
588
|
-
module2.exports = function(
|
|
589
|
-
var _path =
|
|
588
|
+
module2.exports = function(path10, { fs: fs12 }) {
|
|
589
|
+
var _path = path10 || "", _obj = newAttr(), _stat = null;
|
|
590
590
|
function newAttr() {
|
|
591
591
|
return {
|
|
592
592
|
directory: false,
|
|
@@ -597,8 +597,8 @@ var require_fattr = __commonJS({
|
|
|
597
597
|
atime: 0
|
|
598
598
|
};
|
|
599
599
|
}
|
|
600
|
-
if (_path &&
|
|
601
|
-
_stat =
|
|
600
|
+
if (_path && fs12.existsSync(_path)) {
|
|
601
|
+
_stat = fs12.statSync(_path);
|
|
602
602
|
_obj.directory = _stat.isDirectory();
|
|
603
603
|
_obj.mtime = _stat.mtime;
|
|
604
604
|
_obj.atime = _stat.atime;
|
|
@@ -15019,7 +15019,7 @@ var require_BigInteger = __commonJS({
|
|
|
15019
15019
|
var require_bplistParser = __commonJS({
|
|
15020
15020
|
"node_modules/.pnpm/bplist-parser@0.3.2/node_modules/bplist-parser/bplistParser.js"(exports2) {
|
|
15021
15021
|
"use strict";
|
|
15022
|
-
var
|
|
15022
|
+
var fs12 = require("fs");
|
|
15023
15023
|
var bigInt = require_BigInteger();
|
|
15024
15024
|
var debug = false;
|
|
15025
15025
|
exports2.maxObjectSize = 100 * 1e3 * 1e3;
|
|
@@ -15046,7 +15046,7 @@ var require_bplistParser = __commonJS({
|
|
|
15046
15046
|
if (Buffer.isBuffer(fileNameOrBuffer)) {
|
|
15047
15047
|
return tryParseBuffer(fileNameOrBuffer);
|
|
15048
15048
|
}
|
|
15049
|
-
|
|
15049
|
+
fs12.readFile(fileNameOrBuffer, function(err, data) {
|
|
15050
15050
|
if (err) {
|
|
15051
15051
|
reject(err);
|
|
15052
15052
|
return callback(err);
|
|
@@ -15057,7 +15057,7 @@ var require_bplistParser = __commonJS({
|
|
|
15057
15057
|
};
|
|
15058
15058
|
exports2.parseFileSync = function(fileNameOrBuffer) {
|
|
15059
15059
|
if (!Buffer.isBuffer(fileNameOrBuffer)) {
|
|
15060
|
-
fileNameOrBuffer =
|
|
15060
|
+
fileNameOrBuffer = fs12.readFileSync(fileNameOrBuffer);
|
|
15061
15061
|
}
|
|
15062
15062
|
return parseBuffer2(fileNameOrBuffer);
|
|
15063
15063
|
};
|
|
@@ -15347,8 +15347,8 @@ var require_bplistParser = __commonJS({
|
|
|
15347
15347
|
});
|
|
15348
15348
|
|
|
15349
15349
|
// src/engine-main.ts
|
|
15350
|
-
var
|
|
15351
|
-
var
|
|
15350
|
+
var import_node_fs11 = __toESM(require("fs"));
|
|
15351
|
+
var import_node_path9 = __toESM(require("path"));
|
|
15352
15352
|
var import_node_readline = __toESM(require("readline"));
|
|
15353
15353
|
|
|
15354
15354
|
// node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
|
|
@@ -15829,8 +15829,8 @@ function getErrorMap() {
|
|
|
15829
15829
|
|
|
15830
15830
|
// node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
|
|
15831
15831
|
var makeIssue = (params) => {
|
|
15832
|
-
const { data, path:
|
|
15833
|
-
const fullPath = [...
|
|
15832
|
+
const { data, path: path10, errorMaps, issueData } = params;
|
|
15833
|
+
const fullPath = [...path10, ...issueData.path || []];
|
|
15834
15834
|
const fullIssue = {
|
|
15835
15835
|
...issueData,
|
|
15836
15836
|
path: fullPath
|
|
@@ -15946,11 +15946,11 @@ var errorUtil;
|
|
|
15946
15946
|
|
|
15947
15947
|
// node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
|
|
15948
15948
|
var ParseInputLazyPath = class {
|
|
15949
|
-
constructor(parent, value,
|
|
15949
|
+
constructor(parent, value, path10, key) {
|
|
15950
15950
|
this._cachedPath = [];
|
|
15951
15951
|
this.parent = parent;
|
|
15952
15952
|
this.data = value;
|
|
15953
|
-
this._path =
|
|
15953
|
+
this._path = path10;
|
|
15954
15954
|
this._key = key;
|
|
15955
15955
|
}
|
|
15956
15956
|
get path() {
|
|
@@ -19392,11 +19392,12 @@ var coerce = {
|
|
|
19392
19392
|
};
|
|
19393
19393
|
var NEVER = INVALID;
|
|
19394
19394
|
|
|
19395
|
-
// node_modules/.pnpm/@zhuixin+protocol@0.2.
|
|
19395
|
+
// node_modules/.pnpm/@zhuixin+protocol@0.2.6/node_modules/@zhuixin/protocol/dist/index.js
|
|
19396
19396
|
var PROTOCOL_VERSION = "1";
|
|
19397
19397
|
var engineCapabilities = [
|
|
19398
19398
|
"inspect:ipa",
|
|
19399
19399
|
"inspect:mac-app",
|
|
19400
|
+
"inspect:pe",
|
|
19400
19401
|
"extract:app",
|
|
19401
19402
|
"javascript:analyze",
|
|
19402
19403
|
"native:inspect",
|
|
@@ -19472,8 +19473,8 @@ var DEFINITIONS = [
|
|
|
19472
19473
|
id: "javascript:analyze",
|
|
19473
19474
|
label: "Analyze JS",
|
|
19474
19475
|
description: "\u5BF9\u89E3\u5305\u5E94\u7528\u4E2D\u7684 uni-app / WebView \u4E1A\u52A1\u811A\u672C\u505A\u9759\u6001\u5206\u6790\u3002",
|
|
19475
|
-
targetPlatforms: ["ios", "macos"],
|
|
19476
|
-
packageFormats: ["ipa", "app"],
|
|
19476
|
+
targetPlatforms: ["ios", "macos", "windows"],
|
|
19477
|
+
packageFormats: ["ipa", "app", "exe"],
|
|
19477
19478
|
requiresArtifact: true,
|
|
19478
19479
|
requiresRea: true
|
|
19479
19480
|
},
|
|
@@ -19515,13 +19516,12 @@ var DEFINITIONS = [
|
|
|
19515
19516
|
},
|
|
19516
19517
|
{
|
|
19517
19518
|
id: "inspect:pe",
|
|
19518
|
-
label: "Inspect
|
|
19519
|
-
description: "PE
|
|
19519
|
+
label: "Inspect Windows app",
|
|
19520
|
+
description: "Application directory layout, main executable PE headers, sections, imports, .NET clues, version resource, technology hints and Authenticode signature.",
|
|
19520
19521
|
targetPlatforms: ["windows"],
|
|
19521
19522
|
packageFormats: ["exe"],
|
|
19522
19523
|
requiresArtifact: true,
|
|
19523
|
-
|
|
19524
|
-
reason: "Windows Target Provider \u5F85 v1.1"
|
|
19524
|
+
builtIn: true
|
|
19525
19525
|
},
|
|
19526
19526
|
{
|
|
19527
19527
|
id: "inspect:msix",
|
|
@@ -19531,7 +19531,7 @@ var DEFINITIONS = [
|
|
|
19531
19531
|
packageFormats: ["msix", "appx"],
|
|
19532
19532
|
requiresArtifact: true,
|
|
19533
19533
|
available: false,
|
|
19534
|
-
reason: "
|
|
19534
|
+
reason: "Not implemented yet for Windows targets."
|
|
19535
19535
|
},
|
|
19536
19536
|
{
|
|
19537
19537
|
id: "inspect:msi",
|
|
@@ -19541,7 +19541,7 @@ var DEFINITIONS = [
|
|
|
19541
19541
|
packageFormats: ["msi"],
|
|
19542
19542
|
requiresArtifact: true,
|
|
19543
19543
|
available: false,
|
|
19544
|
-
reason: "
|
|
19544
|
+
reason: "Not implemented yet for Windows targets."
|
|
19545
19545
|
},
|
|
19546
19546
|
{
|
|
19547
19547
|
id: "signature:check",
|
|
@@ -19551,7 +19551,7 @@ var DEFINITIONS = [
|
|
|
19551
19551
|
packageFormats: ["exe", "msi", "msix", "appx"],
|
|
19552
19552
|
requiresArtifact: true,
|
|
19553
19553
|
available: false,
|
|
19554
|
-
reason: "Windows
|
|
19554
|
+
reason: "Not implemented yet for Windows targets. inspect:pe already reports the main executable's signature."
|
|
19555
19555
|
}
|
|
19556
19556
|
];
|
|
19557
19557
|
var CapabilityRegistry = class {
|
|
@@ -19688,8 +19688,8 @@ function defaultWorkspaceRoot(hostPlatform, env) {
|
|
|
19688
19688
|
|
|
19689
19689
|
// src/jobs/capability.ts
|
|
19690
19690
|
var import_node_crypto = require("crypto");
|
|
19691
|
-
var
|
|
19692
|
-
var
|
|
19691
|
+
var import_node_fs7 = __toESM(require("fs"));
|
|
19692
|
+
var import_node_path6 = __toESM(require("path"));
|
|
19693
19693
|
|
|
19694
19694
|
// src/errors.ts
|
|
19695
19695
|
var CoreFailure = class extends Error {
|
|
@@ -19950,6 +19950,12 @@ function archName2(cpu, subtype) {
|
|
|
19950
19950
|
if (cpu === 16777228) return (subtype & 16777215) === 2 ? "arm64e" : "arm64";
|
|
19951
19951
|
return `cpu-${cpu.toString(16)}`;
|
|
19952
19952
|
}
|
|
19953
|
+
function readEntitlements(stdout) {
|
|
19954
|
+
const trimmed = stdout.trim();
|
|
19955
|
+
if (!trimmed) return void 0;
|
|
19956
|
+
const xmlStart = trimmed.search(/<\?xml|<plist/i);
|
|
19957
|
+
return (xmlStart === -1 ? trimmed : trimmed.slice(xmlStart)).trim() || void 0;
|
|
19958
|
+
}
|
|
19953
19959
|
function machOArchitectures(filePath) {
|
|
19954
19960
|
const fd = import_node_fs4.default.openSync(filePath, "r");
|
|
19955
19961
|
try {
|
|
@@ -19989,7 +19995,9 @@ async function readSignature(bundlePath, warnings) {
|
|
|
19989
19995
|
try {
|
|
19990
19996
|
const { stdout, stderr } = await execFileAsync(
|
|
19991
19997
|
"codesign",
|
|
19992
|
-
|
|
19998
|
+
// Single `-v` omits the Authority chain on macOS 14.x; the certificate
|
|
19999
|
+
// chain is only printed at triple verbosity.
|
|
20000
|
+
["-dvvv", "--entitlements", ":-", bundlePath],
|
|
19993
20001
|
{ timeout: 3e4, maxBuffer: 4 * 1024 * 1024 }
|
|
19994
20002
|
);
|
|
19995
20003
|
const details = `${stderr}`;
|
|
@@ -19998,11 +20006,14 @@ async function readSignature(bundlePath, warnings) {
|
|
|
19998
20006
|
);
|
|
19999
20007
|
const team = /^TeamIdentifier=(.+)$/m.exec(details)?.[1]?.trim();
|
|
20000
20008
|
const signed = /^CodeDirectory|^Signature=/m.test(details) || authority.length > 0;
|
|
20009
|
+
if (/invalid entitlements blob/i.test(details)) {
|
|
20010
|
+
warnings.push("Code signature reports an invalid entitlements blob; the OS ignores these entitlements.");
|
|
20011
|
+
}
|
|
20001
20012
|
return {
|
|
20002
20013
|
signed,
|
|
20003
20014
|
authority: authority.length ? authority : void 0,
|
|
20004
20015
|
teamIdentifier: team,
|
|
20005
|
-
entitlements: stdout
|
|
20016
|
+
entitlements: readEntitlements(stdout)
|
|
20006
20017
|
};
|
|
20007
20018
|
} catch (error) {
|
|
20008
20019
|
const stderr = error.stderr ?? "";
|
|
@@ -20074,6 +20085,872 @@ async function inspectMacApp(bundlePath) {
|
|
|
20074
20085
|
};
|
|
20075
20086
|
}
|
|
20076
20087
|
|
|
20088
|
+
// src/inspectors/windowsApp.ts
|
|
20089
|
+
var import_node_child_process2 = require("child_process");
|
|
20090
|
+
var import_node_fs6 = __toESM(require("fs"));
|
|
20091
|
+
var import_node_path4 = __toESM(require("path"));
|
|
20092
|
+
var import_node_util2 = require("util");
|
|
20093
|
+
|
|
20094
|
+
// src/inspectors/pe.ts
|
|
20095
|
+
var import_node_fs5 = __toESM(require("fs"));
|
|
20096
|
+
var MACHINES = {
|
|
20097
|
+
332: "x86",
|
|
20098
|
+
34404: "x64",
|
|
20099
|
+
448: "arm",
|
|
20100
|
+
452: "arm (Thumb-2)",
|
|
20101
|
+
43620: "arm64",
|
|
20102
|
+
42561: "arm64ec",
|
|
20103
|
+
42574: "arm64x",
|
|
20104
|
+
512: "ia64"
|
|
20105
|
+
};
|
|
20106
|
+
var SUBSYSTEMS = {
|
|
20107
|
+
1: "native",
|
|
20108
|
+
2: "windows-gui",
|
|
20109
|
+
3: "windows-console",
|
|
20110
|
+
5: "os2-console",
|
|
20111
|
+
7: "posix-console",
|
|
20112
|
+
9: "windows-ce-gui",
|
|
20113
|
+
10: "efi-application",
|
|
20114
|
+
11: "efi-boot-service-driver",
|
|
20115
|
+
12: "efi-runtime-driver",
|
|
20116
|
+
14: "xbox",
|
|
20117
|
+
16: "windows-boot-application"
|
|
20118
|
+
};
|
|
20119
|
+
var CERTIFICATE_TYPES = {
|
|
20120
|
+
1: "x509",
|
|
20121
|
+
2: "pkcs-signed-data",
|
|
20122
|
+
3: "reserved",
|
|
20123
|
+
4: "ts-stack-signed"
|
|
20124
|
+
};
|
|
20125
|
+
var MAX_SECTIONS = 96;
|
|
20126
|
+
var MAX_IMPORT_LIBRARIES = 256;
|
|
20127
|
+
var MAX_FUNCTIONS_PER_LIBRARY = 12;
|
|
20128
|
+
var MAX_THUNKS_PER_LIBRARY = 2e4;
|
|
20129
|
+
var ENTROPY_SAMPLE_BYTES = 4 * 1024 * 1024;
|
|
20130
|
+
var RESOURCE_MAX_DEPTH = 4;
|
|
20131
|
+
function fileReader(fd, size) {
|
|
20132
|
+
return {
|
|
20133
|
+
size,
|
|
20134
|
+
read(offset, length) {
|
|
20135
|
+
if (!Number.isSafeInteger(offset) || offset < 0 || length < 0) return void 0;
|
|
20136
|
+
if (offset + length > size) return void 0;
|
|
20137
|
+
const buffer = Buffer.alloc(length);
|
|
20138
|
+
let done = 0;
|
|
20139
|
+
while (done < length) {
|
|
20140
|
+
const count = import_node_fs5.default.readSync(fd, buffer, done, length - done, offset + done);
|
|
20141
|
+
if (count <= 0) return void 0;
|
|
20142
|
+
done += count;
|
|
20143
|
+
}
|
|
20144
|
+
return buffer;
|
|
20145
|
+
}
|
|
20146
|
+
};
|
|
20147
|
+
}
|
|
20148
|
+
function hex(value) {
|
|
20149
|
+
return `0x${value.toString(16)}`;
|
|
20150
|
+
}
|
|
20151
|
+
function cString(buffer, start = 0, limit = buffer.length) {
|
|
20152
|
+
let end = start;
|
|
20153
|
+
const max = Math.min(limit, buffer.length);
|
|
20154
|
+
while (end < max && buffer[end] !== 0) end += 1;
|
|
20155
|
+
return buffer.toString("latin1", start, end);
|
|
20156
|
+
}
|
|
20157
|
+
function entropyOf(buffer) {
|
|
20158
|
+
if (buffer.length === 0) return 0;
|
|
20159
|
+
const counts = new Array(256).fill(0);
|
|
20160
|
+
for (const byte of buffer) counts[byte] += 1;
|
|
20161
|
+
let entropy = 0;
|
|
20162
|
+
for (const count of counts) {
|
|
20163
|
+
if (count === 0) continue;
|
|
20164
|
+
const p = count / buffer.length;
|
|
20165
|
+
entropy -= p * Math.log2(p);
|
|
20166
|
+
}
|
|
20167
|
+
return Math.round(entropy * 100) / 100;
|
|
20168
|
+
}
|
|
20169
|
+
function isPeFile(filePath) {
|
|
20170
|
+
let fd;
|
|
20171
|
+
try {
|
|
20172
|
+
fd = import_node_fs5.default.openSync(filePath, "r");
|
|
20173
|
+
const head = Buffer.alloc(2);
|
|
20174
|
+
return import_node_fs5.default.readSync(fd, head, 0, 2, 0) === 2 && head[0] === 77 && head[1] === 90;
|
|
20175
|
+
} catch {
|
|
20176
|
+
return false;
|
|
20177
|
+
} finally {
|
|
20178
|
+
if (fd !== void 0) import_node_fs5.default.closeSync(fd);
|
|
20179
|
+
}
|
|
20180
|
+
}
|
|
20181
|
+
function inspectPeFile(filePath) {
|
|
20182
|
+
const fd = import_node_fs5.default.openSync(filePath, "r");
|
|
20183
|
+
try {
|
|
20184
|
+
const size = import_node_fs5.default.fstatSync(fd).size;
|
|
20185
|
+
return inspectPe(fileReader(fd, size));
|
|
20186
|
+
} finally {
|
|
20187
|
+
import_node_fs5.default.closeSync(fd);
|
|
20188
|
+
}
|
|
20189
|
+
}
|
|
20190
|
+
function inspectPe(reader) {
|
|
20191
|
+
const warnings = [];
|
|
20192
|
+
const dos = reader.read(0, 64);
|
|
20193
|
+
if (!dos || dos.readUInt16LE(0) !== 23117) {
|
|
20194
|
+
throw new Error("Not a PE file: the MZ signature is missing.");
|
|
20195
|
+
}
|
|
20196
|
+
const peOffset = dos.readUInt32LE(60);
|
|
20197
|
+
const signature = reader.read(peOffset, 4);
|
|
20198
|
+
if (!signature || signature.toString("latin1") !== "PE\0\0") {
|
|
20199
|
+
throw new Error("Not a PE file: the PE signature is missing.");
|
|
20200
|
+
}
|
|
20201
|
+
const coff = reader.read(peOffset + 4, 20);
|
|
20202
|
+
if (!coff) throw new Error("The COFF header is truncated.");
|
|
20203
|
+
const machine = coff.readUInt16LE(0);
|
|
20204
|
+
const sectionCount = coff.readUInt16LE(2);
|
|
20205
|
+
const timeDateStamp = coff.readUInt32LE(4);
|
|
20206
|
+
const optionalSize = coff.readUInt16LE(16);
|
|
20207
|
+
const characteristics = coff.readUInt16LE(18);
|
|
20208
|
+
const optionalOffset = peOffset + 24;
|
|
20209
|
+
const optional = reader.read(optionalOffset, Math.min(optionalSize, 240));
|
|
20210
|
+
if (!optional || optional.length < 96) {
|
|
20211
|
+
throw new Error("The optional header is truncated.");
|
|
20212
|
+
}
|
|
20213
|
+
const magic = optional.readUInt16LE(0);
|
|
20214
|
+
if (magic !== 267 && magic !== 523) {
|
|
20215
|
+
throw new Error(`Unsupported optional header magic ${hex(magic)}.`);
|
|
20216
|
+
}
|
|
20217
|
+
const plus = magic === 523;
|
|
20218
|
+
const directoriesOffset = plus ? 112 : 96;
|
|
20219
|
+
const entryPointRva = optional.readUInt32LE(16);
|
|
20220
|
+
const imageBase = plus ? optional.readBigUInt64LE(24) : BigInt(optional.readUInt32LE(28));
|
|
20221
|
+
const linker = `${optional[2]}.${optional[3]}`;
|
|
20222
|
+
const minimumOs = `${optional.readUInt16LE(40)}.${optional.readUInt16LE(42)}`;
|
|
20223
|
+
const subsystemId = optional.readUInt16LE(68);
|
|
20224
|
+
const dllCharacteristics = optional.readUInt16LE(70);
|
|
20225
|
+
const declaredDirectories = optional.length >= directoriesOffset ? optional.readUInt32LE(directoriesOffset - 4) : 0;
|
|
20226
|
+
const directory = (index) => {
|
|
20227
|
+
if (index >= declaredDirectories || index >= 16) return void 0;
|
|
20228
|
+
const at = directoriesOffset + index * 8;
|
|
20229
|
+
if (at + 8 > optional.length) return void 0;
|
|
20230
|
+
const rva = optional.readUInt32LE(at);
|
|
20231
|
+
const sizeValue = optional.readUInt32LE(at + 4);
|
|
20232
|
+
return rva === 0 && sizeValue === 0 ? void 0 : { rva, size: sizeValue };
|
|
20233
|
+
};
|
|
20234
|
+
const sections = [];
|
|
20235
|
+
const sectionTableOffset = optionalOffset + optionalSize;
|
|
20236
|
+
const boundedCount = Math.min(sectionCount, MAX_SECTIONS);
|
|
20237
|
+
if (sectionCount > MAX_SECTIONS) {
|
|
20238
|
+
warnings.push(`The file declares ${sectionCount} sections; only the first ${MAX_SECTIONS} were read.`);
|
|
20239
|
+
}
|
|
20240
|
+
for (let index = 0; index < boundedCount; index += 1) {
|
|
20241
|
+
const raw = reader.read(sectionTableOffset + index * 40, 40);
|
|
20242
|
+
if (!raw) {
|
|
20243
|
+
warnings.push("The section table is truncated.");
|
|
20244
|
+
break;
|
|
20245
|
+
}
|
|
20246
|
+
const sectionCharacteristics = raw.readUInt32LE(36);
|
|
20247
|
+
const rawSize = raw.readUInt32LE(16);
|
|
20248
|
+
const rawOffset = raw.readUInt32LE(20);
|
|
20249
|
+
const section = {
|
|
20250
|
+
name: cString(raw, 0, 8),
|
|
20251
|
+
virtualSize: raw.readUInt32LE(8),
|
|
20252
|
+
rawSize,
|
|
20253
|
+
rawOffset,
|
|
20254
|
+
characteristics: sectionCharacteristics,
|
|
20255
|
+
executable: (sectionCharacteristics & 536870912) !== 0,
|
|
20256
|
+
writable: (sectionCharacteristics & 2147483648) !== 0
|
|
20257
|
+
};
|
|
20258
|
+
if (rawSize > 0 && rawOffset > 0) {
|
|
20259
|
+
const sample = reader.read(rawOffset, Math.min(rawSize, ENTROPY_SAMPLE_BYTES));
|
|
20260
|
+
if (sample) section.entropy = entropyOf(sample);
|
|
20261
|
+
}
|
|
20262
|
+
sections.push(section);
|
|
20263
|
+
}
|
|
20264
|
+
const sectionRva = raws(sectionTableOffset, reader, boundedCount);
|
|
20265
|
+
const rvaToOffset = (rva) => {
|
|
20266
|
+
for (const entry of sectionRva) {
|
|
20267
|
+
const span = Math.max(entry.virtualSize, entry.rawSize);
|
|
20268
|
+
if (rva >= entry.rva && rva < entry.rva + span) {
|
|
20269
|
+
const offset = entry.rawOffset + (rva - entry.rva);
|
|
20270
|
+
return offset < reader.size ? offset : void 0;
|
|
20271
|
+
}
|
|
20272
|
+
}
|
|
20273
|
+
return rva < reader.size && sectionRva.every((entry) => rva < entry.rva) ? rva : void 0;
|
|
20274
|
+
};
|
|
20275
|
+
const readRva = (rva, length) => {
|
|
20276
|
+
const offset = rvaToOffset(rva);
|
|
20277
|
+
return offset === void 0 ? void 0 : reader.read(offset, length);
|
|
20278
|
+
};
|
|
20279
|
+
const stringAtRva = (rva, max = 256) => {
|
|
20280
|
+
const offset = rvaToOffset(rva);
|
|
20281
|
+
if (offset === void 0) return void 0;
|
|
20282
|
+
const chunk = reader.read(offset, Math.min(max, reader.size - offset));
|
|
20283
|
+
return chunk ? cString(chunk) : void 0;
|
|
20284
|
+
};
|
|
20285
|
+
const imports = { libraryCount: 0, libraries: [], truncated: false };
|
|
20286
|
+
const importDirectory = directory(1);
|
|
20287
|
+
if (importDirectory) {
|
|
20288
|
+
const thunkSize = plus ? 8 : 4;
|
|
20289
|
+
const collectThunks = (lookupRva, library) => {
|
|
20290
|
+
if (!lookupRva) return;
|
|
20291
|
+
for (let i = 0; i < MAX_THUNKS_PER_LIBRARY; i += 1) {
|
|
20292
|
+
const thunk = readRva(lookupRva + i * thunkSize, thunkSize);
|
|
20293
|
+
if (!thunk) break;
|
|
20294
|
+
const value = plus ? thunk.readBigUInt64LE(0) : BigInt(thunk.readUInt32LE(0));
|
|
20295
|
+
if (value === 0n) break;
|
|
20296
|
+
library.functionCount += 1;
|
|
20297
|
+
if (library.functions.length >= MAX_FUNCTIONS_PER_LIBRARY) continue;
|
|
20298
|
+
const ordinalFlag = plus ? 1n << 63n : 1n << 31n;
|
|
20299
|
+
if (value & ordinalFlag) {
|
|
20300
|
+
library.functions.push(`#${Number(value & 0xffffn)}`);
|
|
20301
|
+
} else {
|
|
20302
|
+
const name = stringAtRva(Number(value & 0x7fffffffn) + 2, 256);
|
|
20303
|
+
if (name) library.functions.push(name);
|
|
20304
|
+
}
|
|
20305
|
+
}
|
|
20306
|
+
};
|
|
20307
|
+
for (let index = 0; index < 4096; index += 1) {
|
|
20308
|
+
const descriptor = readRva(importDirectory.rva + index * 20, 20);
|
|
20309
|
+
if (!descriptor) {
|
|
20310
|
+
if (index === 0) warnings.push("The import table could not be read.");
|
|
20311
|
+
break;
|
|
20312
|
+
}
|
|
20313
|
+
const nameRva = descriptor.readUInt32LE(12);
|
|
20314
|
+
const lookupRva = descriptor.readUInt32LE(0) || descriptor.readUInt32LE(16);
|
|
20315
|
+
if (nameRva === 0 && lookupRva === 0) break;
|
|
20316
|
+
imports.libraryCount += 1;
|
|
20317
|
+
if (imports.libraries.length >= MAX_IMPORT_LIBRARIES) {
|
|
20318
|
+
imports.truncated = true;
|
|
20319
|
+
continue;
|
|
20320
|
+
}
|
|
20321
|
+
const library = {
|
|
20322
|
+
name: stringAtRva(nameRva, 260) ?? "?",
|
|
20323
|
+
functionCount: 0,
|
|
20324
|
+
functions: [],
|
|
20325
|
+
delayLoaded: false
|
|
20326
|
+
};
|
|
20327
|
+
collectThunks(lookupRva, library);
|
|
20328
|
+
imports.libraries.push(library);
|
|
20329
|
+
}
|
|
20330
|
+
}
|
|
20331
|
+
const delayDirectory = directory(13);
|
|
20332
|
+
if (delayDirectory) {
|
|
20333
|
+
for (let index = 0; index < 64; index += 1) {
|
|
20334
|
+
const descriptor = readRva(delayDirectory.rva + index * 32, 32);
|
|
20335
|
+
if (!descriptor) break;
|
|
20336
|
+
const attributes = descriptor.readUInt32LE(0);
|
|
20337
|
+
const nameField = descriptor.readUInt32LE(4);
|
|
20338
|
+
if (nameField === 0) break;
|
|
20339
|
+
const nameRva = attributes & 1 ? nameField : nameField - Number(imageBase & 0xffffffffn);
|
|
20340
|
+
const name = stringAtRva(nameRva, 260);
|
|
20341
|
+
if (!name) continue;
|
|
20342
|
+
imports.libraryCount += 1;
|
|
20343
|
+
if (imports.libraries.length < MAX_IMPORT_LIBRARIES) {
|
|
20344
|
+
imports.libraries.push({ name, functionCount: 0, functions: [], delayLoaded: true });
|
|
20345
|
+
} else {
|
|
20346
|
+
imports.truncated = true;
|
|
20347
|
+
}
|
|
20348
|
+
}
|
|
20349
|
+
}
|
|
20350
|
+
const exportsInfo = { count: 0, namedCount: 0 };
|
|
20351
|
+
const exportDirectory = directory(0);
|
|
20352
|
+
if (exportDirectory) {
|
|
20353
|
+
const table = readRva(exportDirectory.rva, 40);
|
|
20354
|
+
if (table) {
|
|
20355
|
+
exportsInfo.count = table.readUInt32LE(20);
|
|
20356
|
+
exportsInfo.namedCount = table.readUInt32LE(24);
|
|
20357
|
+
exportsInfo.dllName = stringAtRva(table.readUInt32LE(12), 260);
|
|
20358
|
+
}
|
|
20359
|
+
}
|
|
20360
|
+
let dotnet;
|
|
20361
|
+
const clrDirectory = directory(14);
|
|
20362
|
+
if (clrDirectory) {
|
|
20363
|
+
const header = readRva(clrDirectory.rva, 72);
|
|
20364
|
+
if (header) {
|
|
20365
|
+
const flags = header.readUInt32LE(16);
|
|
20366
|
+
dotnet = {
|
|
20367
|
+
runtimeVersion: `${header.readUInt16LE(4)}.${header.readUInt16LE(6)}`,
|
|
20368
|
+
ilOnly: (flags & 1) !== 0,
|
|
20369
|
+
requires32Bit: (flags & 2) !== 0,
|
|
20370
|
+
strongNameSigned: (flags & 8) !== 0,
|
|
20371
|
+
nativeEntryPoint: (flags & 16) !== 0
|
|
20372
|
+
};
|
|
20373
|
+
const metadataRva = header.readUInt32LE(8);
|
|
20374
|
+
const root = metadataRva ? readRva(metadataRva, 32) : void 0;
|
|
20375
|
+
if (root && root.readUInt32LE(0) === 1112167234) {
|
|
20376
|
+
const versionLength = root.readUInt32LE(12);
|
|
20377
|
+
if (versionLength > 0 && versionLength <= 255) {
|
|
20378
|
+
const version2 = readRva(metadataRva + 16, versionLength);
|
|
20379
|
+
if (version2) dotnet.metadataVersion = cString(version2);
|
|
20380
|
+
}
|
|
20381
|
+
}
|
|
20382
|
+
}
|
|
20383
|
+
}
|
|
20384
|
+
const version = readVersionInfo(directory(2), readRva, rvaToOffset, reader, warnings);
|
|
20385
|
+
let debug;
|
|
20386
|
+
const debugDirectory = directory(6);
|
|
20387
|
+
if (debugDirectory) {
|
|
20388
|
+
const entries = Math.min(Math.floor(debugDirectory.size / 28), 16);
|
|
20389
|
+
for (let index = 0; index < entries && !debug; index += 1) {
|
|
20390
|
+
const entry = readRva(debugDirectory.rva + index * 28, 28);
|
|
20391
|
+
if (!entry || entry.readUInt32LE(12) !== 2) continue;
|
|
20392
|
+
const dataSize = entry.readUInt32LE(16);
|
|
20393
|
+
const pointer = entry.readUInt32LE(24);
|
|
20394
|
+
const data = pointer && dataSize >= 24 && dataSize < 4096 ? reader.read(pointer, dataSize) : void 0;
|
|
20395
|
+
if (data && data.toString("latin1", 0, 4) === "RSDS") {
|
|
20396
|
+
debug = {
|
|
20397
|
+
guid: guidString(data.subarray(4, 20)),
|
|
20398
|
+
age: data.readUInt32LE(20),
|
|
20399
|
+
pdbPath: cString(data, 24)
|
|
20400
|
+
};
|
|
20401
|
+
}
|
|
20402
|
+
}
|
|
20403
|
+
}
|
|
20404
|
+
const certificate = { present: false, sizeBytes: 0 };
|
|
20405
|
+
const certificateEntry = directory(4);
|
|
20406
|
+
if (certificateEntry) {
|
|
20407
|
+
certificate.sizeBytes = certificateEntry.size;
|
|
20408
|
+
const header = reader.read(certificateEntry.rva, 8);
|
|
20409
|
+
if (header) {
|
|
20410
|
+
certificate.present = true;
|
|
20411
|
+
certificate.type = CERTIFICATE_TYPES[header.readUInt16LE(6)] ?? `type-${header.readUInt16LE(6)}`;
|
|
20412
|
+
} else {
|
|
20413
|
+
warnings.push("The certificate table points outside the file.");
|
|
20414
|
+
}
|
|
20415
|
+
}
|
|
20416
|
+
let endOfImage = 0;
|
|
20417
|
+
for (const section of sections) {
|
|
20418
|
+
endOfImage = Math.max(endOfImage, section.rawOffset + section.rawSize);
|
|
20419
|
+
}
|
|
20420
|
+
const certificateStart = certificate.present ? certificateEntry.rva : reader.size;
|
|
20421
|
+
const overlayEnd = Math.min(reader.size, certificateStart);
|
|
20422
|
+
const overlaySizeBytes = Math.max(0, overlayEnd - endOfImage);
|
|
20423
|
+
const mitigations = [];
|
|
20424
|
+
if (dllCharacteristics & 64) mitigations.push("ASLR");
|
|
20425
|
+
if (dllCharacteristics & 32) mitigations.push("high-entropy-ASLR");
|
|
20426
|
+
if (dllCharacteristics & 256) mitigations.push("DEP");
|
|
20427
|
+
if (dllCharacteristics & 16384) mitigations.push("CFG");
|
|
20428
|
+
if (dllCharacteristics & 1024) mitigations.push("no-SEH");
|
|
20429
|
+
if (dllCharacteristics & 4096) mitigations.push("app-container");
|
|
20430
|
+
const machineName = MACHINES[machine] ?? `machine-${hex(machine)}`;
|
|
20431
|
+
const reproducible = debugHasReproFlag(debugDirectory, readRva) || timeDateStamp === 0;
|
|
20432
|
+
const timestamp = timeDateStamp !== 0 && !reproducible ? new Date(timeDateStamp * 1e3).toISOString() : void 0;
|
|
20433
|
+
return {
|
|
20434
|
+
kind: "pe",
|
|
20435
|
+
sizeBytes: reader.size,
|
|
20436
|
+
format: plus ? "PE32+" : "PE32",
|
|
20437
|
+
architecture: machineName,
|
|
20438
|
+
isDll: (characteristics & 8192) !== 0,
|
|
20439
|
+
subsystem: SUBSYSTEMS[subsystemId] ?? `subsystem-${subsystemId}`,
|
|
20440
|
+
timestamp,
|
|
20441
|
+
timestampIsReproducible: reproducible,
|
|
20442
|
+
entryPointRva,
|
|
20443
|
+
imageBase: hex(imageBase),
|
|
20444
|
+
linkerVersion: linker,
|
|
20445
|
+
minimumOsVersion: minimumOs,
|
|
20446
|
+
mitigations,
|
|
20447
|
+
sections,
|
|
20448
|
+
imports,
|
|
20449
|
+
exports: exportsInfo,
|
|
20450
|
+
dotnet,
|
|
20451
|
+
version,
|
|
20452
|
+
debug,
|
|
20453
|
+
certificate,
|
|
20454
|
+
overlaySizeBytes,
|
|
20455
|
+
warnings
|
|
20456
|
+
};
|
|
20457
|
+
}
|
|
20458
|
+
function raws(tableOffset, reader, count) {
|
|
20459
|
+
const result = [];
|
|
20460
|
+
for (let index = 0; index < count; index += 1) {
|
|
20461
|
+
const raw = reader.read(tableOffset + index * 40, 40);
|
|
20462
|
+
if (!raw) break;
|
|
20463
|
+
result.push({
|
|
20464
|
+
virtualSize: raw.readUInt32LE(8),
|
|
20465
|
+
rva: raw.readUInt32LE(12),
|
|
20466
|
+
rawSize: raw.readUInt32LE(16),
|
|
20467
|
+
rawOffset: raw.readUInt32LE(20)
|
|
20468
|
+
});
|
|
20469
|
+
}
|
|
20470
|
+
return result;
|
|
20471
|
+
}
|
|
20472
|
+
function guidString(bytes) {
|
|
20473
|
+
const part = (start, end) => [...bytes.subarray(start, end)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
20474
|
+
const reverse = (start, end) => [...bytes.subarray(start, end)].reverse().map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
20475
|
+
return `${reverse(0, 4)}-${reverse(4, 6)}-${reverse(6, 8)}-${part(8, 10)}-${part(10, 16)}`;
|
|
20476
|
+
}
|
|
20477
|
+
function debugHasReproFlag(directory, readRva) {
|
|
20478
|
+
if (!directory) return false;
|
|
20479
|
+
const entries = Math.min(Math.floor(directory.size / 28), 16);
|
|
20480
|
+
for (let index = 0; index < entries; index += 1) {
|
|
20481
|
+
const entry = readRva(directory.rva + index * 28, 28);
|
|
20482
|
+
if (entry && entry.readUInt32LE(12) === 16) return true;
|
|
20483
|
+
}
|
|
20484
|
+
return false;
|
|
20485
|
+
}
|
|
20486
|
+
function readVersionInfo(resources, readRva, rvaToOffset, reader, warnings) {
|
|
20487
|
+
if (!resources) return void 0;
|
|
20488
|
+
const base = resources.rva;
|
|
20489
|
+
const readDirectory = (offset2) => {
|
|
20490
|
+
const header = readRva(base + offset2, 16);
|
|
20491
|
+
if (!header) return void 0;
|
|
20492
|
+
const named = header.readUInt16LE(12);
|
|
20493
|
+
const ids = header.readUInt16LE(14);
|
|
20494
|
+
const count = Math.min(named + ids, 512);
|
|
20495
|
+
const table = count ? readRva(base + offset2 + 16, count * 8) : Buffer.alloc(0);
|
|
20496
|
+
if (!table) return void 0;
|
|
20497
|
+
const entries = [];
|
|
20498
|
+
for (let index = 0; index < count; index += 1) {
|
|
20499
|
+
const nameOrId = table.readUInt32LE(index * 8);
|
|
20500
|
+
const target = table.readUInt32LE(index * 8 + 4);
|
|
20501
|
+
entries.push({
|
|
20502
|
+
id: nameOrId & 2147483647,
|
|
20503
|
+
named: (nameOrId & 2147483648) !== 0,
|
|
20504
|
+
isDirectory: (target & 2147483648) !== 0,
|
|
20505
|
+
target: target & 2147483647
|
|
20506
|
+
});
|
|
20507
|
+
}
|
|
20508
|
+
return entries;
|
|
20509
|
+
};
|
|
20510
|
+
const root = readDirectory(0);
|
|
20511
|
+
const typeEntry = root?.find((entry) => !entry.named && entry.id === 16 && entry.isDirectory);
|
|
20512
|
+
if (!typeEntry) return void 0;
|
|
20513
|
+
let level = readDirectory(typeEntry.target);
|
|
20514
|
+
let depth = 1;
|
|
20515
|
+
let leaf;
|
|
20516
|
+
while (level && level.length > 0 && depth <= RESOURCE_MAX_DEPTH) {
|
|
20517
|
+
const entry = level[0];
|
|
20518
|
+
if (!entry.isDirectory) {
|
|
20519
|
+
leaf = entry;
|
|
20520
|
+
break;
|
|
20521
|
+
}
|
|
20522
|
+
level = readDirectory(entry.target);
|
|
20523
|
+
depth += 1;
|
|
20524
|
+
}
|
|
20525
|
+
if (!leaf) return void 0;
|
|
20526
|
+
const dataEntry = readRva(base + leaf.target, 16);
|
|
20527
|
+
if (!dataEntry) return void 0;
|
|
20528
|
+
const dataRva = dataEntry.readUInt32LE(0);
|
|
20529
|
+
const dataSize = dataEntry.readUInt32LE(4);
|
|
20530
|
+
if (dataSize < 8 || dataSize > 256 * 1024) {
|
|
20531
|
+
warnings.push("The version resource has an unexpected size and was skipped.");
|
|
20532
|
+
return void 0;
|
|
20533
|
+
}
|
|
20534
|
+
const offset = rvaToOffset(dataRva);
|
|
20535
|
+
const data = offset === void 0 ? void 0 : reader.read(offset, dataSize);
|
|
20536
|
+
if (!data) return void 0;
|
|
20537
|
+
return parseVersionResource(data);
|
|
20538
|
+
}
|
|
20539
|
+
function align4(value) {
|
|
20540
|
+
return value + 3 & ~3;
|
|
20541
|
+
}
|
|
20542
|
+
function readVersionBlock(buffer, offset, limit) {
|
|
20543
|
+
if (offset + 6 > limit) return void 0;
|
|
20544
|
+
const length = buffer.readUInt16LE(offset);
|
|
20545
|
+
const valueLength = buffer.readUInt16LE(offset + 2);
|
|
20546
|
+
const type = buffer.readUInt16LE(offset + 4);
|
|
20547
|
+
if (length < 6 || offset + length > limit) return void 0;
|
|
20548
|
+
let cursor = offset + 6;
|
|
20549
|
+
let key = "";
|
|
20550
|
+
while (cursor + 2 <= offset + length) {
|
|
20551
|
+
const unit = buffer.readUInt16LE(cursor);
|
|
20552
|
+
cursor += 2;
|
|
20553
|
+
if (unit === 0) break;
|
|
20554
|
+
key += String.fromCharCode(unit);
|
|
20555
|
+
if (key.length > 64) return void 0;
|
|
20556
|
+
}
|
|
20557
|
+
const valueOffset = align4(cursor);
|
|
20558
|
+
const valueBytes = type === 1 ? valueLength * 2 : valueLength;
|
|
20559
|
+
return {
|
|
20560
|
+
key,
|
|
20561
|
+
valueOffset,
|
|
20562
|
+
valueLength,
|
|
20563
|
+
type,
|
|
20564
|
+
childrenOffset: align4(valueOffset + valueBytes),
|
|
20565
|
+
end: offset + length
|
|
20566
|
+
};
|
|
20567
|
+
}
|
|
20568
|
+
var VERSION_FIELDS = {
|
|
20569
|
+
CompanyName: "companyName",
|
|
20570
|
+
FileDescription: "fileDescription",
|
|
20571
|
+
FileVersion: "fileVersion",
|
|
20572
|
+
InternalName: "internalName",
|
|
20573
|
+
LegalCopyright: "legalCopyright",
|
|
20574
|
+
OriginalFilename: "originalFilename",
|
|
20575
|
+
ProductName: "productName",
|
|
20576
|
+
ProductVersion: "productVersion"
|
|
20577
|
+
};
|
|
20578
|
+
function parseVersionResource(data) {
|
|
20579
|
+
const root = readVersionBlock(data, 0, data.length);
|
|
20580
|
+
if (!root || root.key !== "VS_VERSION_INFO") return void 0;
|
|
20581
|
+
const result = {};
|
|
20582
|
+
let cursor = root.childrenOffset;
|
|
20583
|
+
while (cursor < root.end) {
|
|
20584
|
+
const group = readVersionBlock(data, cursor, root.end);
|
|
20585
|
+
if (!group) break;
|
|
20586
|
+
if (group.key === "StringFileInfo") {
|
|
20587
|
+
let tableCursor = group.childrenOffset;
|
|
20588
|
+
const table = readVersionBlock(data, tableCursor, group.end);
|
|
20589
|
+
if (table) {
|
|
20590
|
+
tableCursor = table.childrenOffset;
|
|
20591
|
+
while (tableCursor < table.end) {
|
|
20592
|
+
const entry = readVersionBlock(data, tableCursor, table.end);
|
|
20593
|
+
if (!entry) break;
|
|
20594
|
+
const field = VERSION_FIELDS[entry.key];
|
|
20595
|
+
if (field && entry.type === 1 && entry.valueLength > 0) {
|
|
20596
|
+
const raw = data.toString(
|
|
20597
|
+
"utf16le",
|
|
20598
|
+
entry.valueOffset,
|
|
20599
|
+
Math.min(entry.valueOffset + entry.valueLength * 2, entry.end)
|
|
20600
|
+
);
|
|
20601
|
+
const value = raw.replace(/\0+$/, "").trim();
|
|
20602
|
+
if (value) result[field] = value.slice(0, 256);
|
|
20603
|
+
}
|
|
20604
|
+
tableCursor = align4(entry.end);
|
|
20605
|
+
}
|
|
20606
|
+
}
|
|
20607
|
+
}
|
|
20608
|
+
cursor = align4(group.end);
|
|
20609
|
+
}
|
|
20610
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
20611
|
+
}
|
|
20612
|
+
|
|
20613
|
+
// src/inspectors/windowsApp.ts
|
|
20614
|
+
var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
|
|
20615
|
+
var WALK_ENTRY_LIMIT = 2e4;
|
|
20616
|
+
var WALK_DEPTH_LIMIT = 4;
|
|
20617
|
+
var HELPER_EXECUTABLE = /^(unins\d*|uninstall.*|update|squirrel|setup.*|install.*|vc_?redist.*|vcredist.*|dxsetup|crashpad_handler|crashreporter.*|crashsender.*|notification_helper|elevate|createdump|.*helper.*|.*updater.*)\.exe$/i;
|
|
20618
|
+
function normalizeName(value) {
|
|
20619
|
+
return value.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
20620
|
+
}
|
|
20621
|
+
function relative(root, target) {
|
|
20622
|
+
const value = import_node_path4.default.relative(root, target).split(import_node_path4.default.sep).join("/");
|
|
20623
|
+
return value === "" ? "." : value;
|
|
20624
|
+
}
|
|
20625
|
+
function walk(root) {
|
|
20626
|
+
const files = [];
|
|
20627
|
+
const directories = [];
|
|
20628
|
+
let seen = 0;
|
|
20629
|
+
let truncated = false;
|
|
20630
|
+
const visit = (dir, depth) => {
|
|
20631
|
+
let entries;
|
|
20632
|
+
try {
|
|
20633
|
+
entries = import_node_fs6.default.readdirSync(dir, { withFileTypes: true });
|
|
20634
|
+
} catch {
|
|
20635
|
+
return;
|
|
20636
|
+
}
|
|
20637
|
+
for (const entry of entries) {
|
|
20638
|
+
if (seen >= WALK_ENTRY_LIMIT) {
|
|
20639
|
+
truncated = true;
|
|
20640
|
+
return;
|
|
20641
|
+
}
|
|
20642
|
+
seen += 1;
|
|
20643
|
+
if (entry.isSymbolicLink()) continue;
|
|
20644
|
+
const full = import_node_path4.default.join(dir, entry.name);
|
|
20645
|
+
const rel = relative(root, full);
|
|
20646
|
+
if (entry.isDirectory()) {
|
|
20647
|
+
directories.push(rel.toLowerCase());
|
|
20648
|
+
if (depth < WALK_DEPTH_LIMIT) visit(full, depth + 1);
|
|
20649
|
+
} else if (entry.isFile()) {
|
|
20650
|
+
let size = 0;
|
|
20651
|
+
try {
|
|
20652
|
+
size = import_node_fs6.default.statSync(full).size;
|
|
20653
|
+
} catch {
|
|
20654
|
+
}
|
|
20655
|
+
files.push({ relative: rel, lower: rel.toLowerCase(), size });
|
|
20656
|
+
}
|
|
20657
|
+
}
|
|
20658
|
+
};
|
|
20659
|
+
visit(root, 0);
|
|
20660
|
+
return { files, directories, truncated };
|
|
20661
|
+
}
|
|
20662
|
+
function topLevelExecutables(dir) {
|
|
20663
|
+
try {
|
|
20664
|
+
return import_node_fs6.default.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && /\.exe$/i.test(entry.name)).map((entry) => entry.name);
|
|
20665
|
+
} catch {
|
|
20666
|
+
return [];
|
|
20667
|
+
}
|
|
20668
|
+
}
|
|
20669
|
+
function newestSquirrelVersion(dir) {
|
|
20670
|
+
try {
|
|
20671
|
+
const versions = import_node_fs6.default.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && /^app-\d/i.test(entry.name)).map((entry) => entry.name);
|
|
20672
|
+
versions.sort(
|
|
20673
|
+
(a, b) => a.localeCompare(b, void 0, { numeric: true, sensitivity: "base" })
|
|
20674
|
+
);
|
|
20675
|
+
return versions.length ? versions[versions.length - 1] : void 0;
|
|
20676
|
+
} catch {
|
|
20677
|
+
return void 0;
|
|
20678
|
+
}
|
|
20679
|
+
}
|
|
20680
|
+
function findWindowsElectronRoot(appPath) {
|
|
20681
|
+
let base = appPath;
|
|
20682
|
+
try {
|
|
20683
|
+
if (import_node_fs6.default.statSync(appPath).isFile()) base = import_node_path4.default.dirname(appPath);
|
|
20684
|
+
} catch {
|
|
20685
|
+
return void 0;
|
|
20686
|
+
}
|
|
20687
|
+
const roots = [base];
|
|
20688
|
+
const squirrel = newestSquirrelVersion(base);
|
|
20689
|
+
if (squirrel) roots.push(import_node_path4.default.join(base, squirrel));
|
|
20690
|
+
for (const root of roots) {
|
|
20691
|
+
const asar = import_node_path4.default.join(root, "resources", "app.asar");
|
|
20692
|
+
if (import_node_fs6.default.existsSync(asar)) return asar;
|
|
20693
|
+
const unpacked = import_node_path4.default.join(root, "resources", "app");
|
|
20694
|
+
if (import_node_fs6.default.existsSync(import_node_path4.default.join(unpacked, "package.json"))) return unpacked;
|
|
20695
|
+
}
|
|
20696
|
+
return void 0;
|
|
20697
|
+
}
|
|
20698
|
+
function rankExecutables(root, names, appName) {
|
|
20699
|
+
const wanted = normalizeName(appName);
|
|
20700
|
+
const scored = names.map((name) => {
|
|
20701
|
+
let size = 0;
|
|
20702
|
+
try {
|
|
20703
|
+
size = import_node_fs6.default.statSync(import_node_path4.default.join(root, name)).size;
|
|
20704
|
+
} catch {
|
|
20705
|
+
}
|
|
20706
|
+
let score = Math.log10(size + 1);
|
|
20707
|
+
if (HELPER_EXECUTABLE.test(name)) score -= 100;
|
|
20708
|
+
if (wanted && normalizeName(name.replace(/\.exe$/i, "")) === wanted) score += 50;
|
|
20709
|
+
return { name, score };
|
|
20710
|
+
});
|
|
20711
|
+
scored.sort((a, b) => b.score - a.score || a.name.localeCompare(b.name));
|
|
20712
|
+
return scored.map((item) => item.name);
|
|
20713
|
+
}
|
|
20714
|
+
var FILE_MARKERS = [
|
|
20715
|
+
{ hint: "CEF (Chromium Embedded Framework)", test: (f) => /(^|\/)libcef\.dll$/.test(f), basis: "libcef.dll" },
|
|
20716
|
+
{ hint: "WebView2", test: (f) => /(^|\/)webview2loader\.dll$/.test(f), basis: "WebView2Loader.dll" },
|
|
20717
|
+
{ hint: "Qt", test: (f) => /(^|\/)qt\d?core(d)?\.dll$/.test(f), basis: "QtCore library" },
|
|
20718
|
+
{ hint: "Flutter", test: (f) => /(^|\/)flutter_windows\.dll$/.test(f), basis: "flutter_windows.dll" },
|
|
20719
|
+
{ hint: "Unity", test: (f) => /(^|\/)unityplayer\.dll$/.test(f), basis: "UnityPlayer.dll" },
|
|
20720
|
+
{ hint: "NW.js", test: (f) => /(^|\/)nw(_elf)?\.dll$/.test(f), basis: "nw.dll" },
|
|
20721
|
+
{ hint: "Java", test: (f) => /(^|\/)jvm\.dll$/.test(f), basis: "bundled jvm.dll" },
|
|
20722
|
+
{ hint: "Python", test: (f) => /(^|\/)python3\d*\.dll$/.test(f), basis: "python3 runtime DLL" },
|
|
20723
|
+
{ hint: "PyInstaller", test: (f) => /(^|\/)base_library\.zip$/.test(f), basis: "base_library.zip" },
|
|
20724
|
+
{ hint: ".NET (modern runtime)", test: (f) => /(^|\/)(hostfxr|coreclr)\.dll$/.test(f) || /\.runtimeconfig\.json$/.test(f), basis: "hostfxr/coreclr or runtimeconfig.json" },
|
|
20725
|
+
{ hint: "WPF", test: (f) => /(^|\/)presentationframework\.dll$/.test(f), basis: "PresentationFramework.dll" },
|
|
20726
|
+
{ hint: "Windows Forms", test: (f) => /(^|\/)system\.windows\.forms\.dll$/.test(f), basis: "System.Windows.Forms.dll" },
|
|
20727
|
+
{ hint: "WinUI", test: (f) => /(^|\/)microsoft\.ui\.xaml\.dll$/.test(f), basis: "Microsoft.UI.Xaml.dll" },
|
|
20728
|
+
{ hint: "Squirrel installer", test: (f) => f === "update.exe", basis: "Update.exe" },
|
|
20729
|
+
{ hint: "Inno Setup uninstaller", test: (f) => /(^|\/)unins\d+\.exe$/.test(f), basis: "unins000.exe" },
|
|
20730
|
+
{ hint: "NSIS uninstaller", test: (f) => /(^|\/)uninstall[^/]*\.exe$/.test(f), basis: "Uninstall*.exe" }
|
|
20731
|
+
];
|
|
20732
|
+
var BINARY_MARKERS = [
|
|
20733
|
+
{ hint: "Go", needle: Buffer.from("Go buildinf:", "latin1"), basis: "Go build info in the executable" },
|
|
20734
|
+
{ hint: "Rust", needle: Buffer.from("/rustc/", "latin1"), basis: "rustc source paths in the executable" },
|
|
20735
|
+
{ hint: "Tauri", needle: Buffer.from("tauri-runtime", "latin1"), basis: "tauri-runtime crate paths in the executable" },
|
|
20736
|
+
{ hint: "Wails", needle: Buffer.from("wailsapp/wails", "latin1"), basis: "wails module path in the executable" },
|
|
20737
|
+
{ hint: "PyInstaller", needle: Buffer.from("PyInstaller", "latin1"), basis: "PyInstaller bootloader strings" },
|
|
20738
|
+
{ hint: "Electron", needle: Buffer.from("dL7pKGdnNz796PbbjQWNKmHXBZaB9tsX", "latin1"), basis: "Electron fuse sentinel in the executable" }
|
|
20739
|
+
];
|
|
20740
|
+
var BINARY_SCAN_LIMIT = 192 * 1024 * 1024;
|
|
20741
|
+
var BINARY_SCAN_CHUNK = 8 * 1024 * 1024;
|
|
20742
|
+
function scanBinaryMarkers(filePath) {
|
|
20743
|
+
const found = [];
|
|
20744
|
+
let fd;
|
|
20745
|
+
try {
|
|
20746
|
+
fd = import_node_fs6.default.openSync(filePath, "r");
|
|
20747
|
+
const size = import_node_fs6.default.fstatSync(fd).size;
|
|
20748
|
+
const overlap = Math.max(...BINARY_MARKERS.map((marker) => marker.needle.length));
|
|
20749
|
+
const buffer = Buffer.alloc(BINARY_SCAN_CHUNK + overlap);
|
|
20750
|
+
let position = 0;
|
|
20751
|
+
let carried = 0;
|
|
20752
|
+
while (position < Math.min(size, BINARY_SCAN_LIMIT)) {
|
|
20753
|
+
const read = import_node_fs6.default.readSync(fd, buffer, carried, BINARY_SCAN_CHUNK, position);
|
|
20754
|
+
if (read <= 0) break;
|
|
20755
|
+
const view = buffer.subarray(0, carried + read);
|
|
20756
|
+
for (const marker of BINARY_MARKERS) {
|
|
20757
|
+
if (!found.some((item) => item.hint === marker.hint) && view.includes(marker.needle)) {
|
|
20758
|
+
found.push({ hint: marker.hint, basis: marker.basis });
|
|
20759
|
+
}
|
|
20760
|
+
}
|
|
20761
|
+
position += read;
|
|
20762
|
+
carried = Math.min(overlap - 1, view.length);
|
|
20763
|
+
view.copy(buffer, 0, view.length - carried);
|
|
20764
|
+
}
|
|
20765
|
+
} catch {
|
|
20766
|
+
} finally {
|
|
20767
|
+
if (fd !== void 0) import_node_fs6.default.closeSync(fd);
|
|
20768
|
+
}
|
|
20769
|
+
return found;
|
|
20770
|
+
}
|
|
20771
|
+
function detectTechnology(files, directories, electron, pe, mainPath) {
|
|
20772
|
+
const evidence = [];
|
|
20773
|
+
const add = (hint, basis) => {
|
|
20774
|
+
if (!evidence.some((item) => item.hint === hint)) evidence.push({ hint, basis });
|
|
20775
|
+
};
|
|
20776
|
+
if (electron.asar) add("Electron", "resources/app.asar");
|
|
20777
|
+
else if (electron.unpackedApp) add("Electron", "resources/app");
|
|
20778
|
+
const lower = files.map((file) => file.lower);
|
|
20779
|
+
if (!electron.asar && !electron.unpackedApp && lower.includes("chrome_100_percent.pak") && lower.includes("resources.pak")) {
|
|
20780
|
+
add("Chromium-based", "chrome_100_percent.pak and resources.pak");
|
|
20781
|
+
}
|
|
20782
|
+
for (const marker of FILE_MARKERS) {
|
|
20783
|
+
if (lower.some(marker.test)) add(marker.hint, marker.basis);
|
|
20784
|
+
}
|
|
20785
|
+
if (directories.some((dir) => /(^|\/)[^/]+_data$/.test(dir)) && lower.some((f) => /(^|\/)unityplayer\.dll$/.test(f))) {
|
|
20786
|
+
add("Unity", "*_Data directory");
|
|
20787
|
+
}
|
|
20788
|
+
if (pe?.dotnet) {
|
|
20789
|
+
add(".NET", pe.dotnet.metadataVersion ? `CLR header (${pe.dotnet.metadataVersion})` : "CLR header");
|
|
20790
|
+
}
|
|
20791
|
+
if (pe?.imports.libraries.some((library) => /^mscoree\.dll$/i.test(library.name))) {
|
|
20792
|
+
add(".NET Framework", "imports mscoree.dll");
|
|
20793
|
+
}
|
|
20794
|
+
const libraries = pe?.imports.libraries.map((library) => library.name.toLowerCase()) ?? [];
|
|
20795
|
+
if (libraries.some((name) => /^mfc\d+/.test(name))) add("MFC", "imports mfc*.dll");
|
|
20796
|
+
if (libraries.some((name) => /^qt\d?(core|gui|widgets)/.test(name))) add("Qt", "imports Qt libraries");
|
|
20797
|
+
if (libraries.some((name) => /^webview2loader\.dll$/.test(name))) add("WebView2", "imports WebView2Loader.dll");
|
|
20798
|
+
if (mainPath) {
|
|
20799
|
+
for (const found of scanBinaryMarkers(mainPath)) add(found.hint, found.basis);
|
|
20800
|
+
}
|
|
20801
|
+
return { hints: evidence.map((item) => item.hint), evidence };
|
|
20802
|
+
}
|
|
20803
|
+
function powershellPath() {
|
|
20804
|
+
const root = process.env.SystemRoot || "C:\\Windows";
|
|
20805
|
+
return import_node_path4.default.join(root, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
20806
|
+
}
|
|
20807
|
+
async function verifyAuthenticode(filePath, warnings) {
|
|
20808
|
+
if (process.platform !== "win32") {
|
|
20809
|
+
warnings.push("Authenticode verification requires a Windows host; only the embedded signature block was read.");
|
|
20810
|
+
return void 0;
|
|
20811
|
+
}
|
|
20812
|
+
const script = "$s = Get-AuthenticodeSignature -LiteralPath $env:APPTRACE_SIGNATURE_PATH; [pscustomobject]@{ Status = [string]$s.Status; Message = $s.StatusMessage; Subject = $s.SignerCertificate.Subject; Issuer = $s.SignerCertificate.Issuer; Thumbprint = $s.SignerCertificate.Thumbprint; NotAfter = if ($s.SignerCertificate) { $s.SignerCertificate.NotAfter.ToString('o') } else { $null }; Timestamper = $s.TimeStamperCertificate.Subject } | ConvertTo-Json -Compress";
|
|
20813
|
+
try {
|
|
20814
|
+
const { stdout } = await execFileAsync2(
|
|
20815
|
+
powershellPath(),
|
|
20816
|
+
["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", script],
|
|
20817
|
+
{
|
|
20818
|
+
timeout: 45e3,
|
|
20819
|
+
maxBuffer: 1024 * 1024,
|
|
20820
|
+
windowsHide: true,
|
|
20821
|
+
env: { ...process.env, APPTRACE_SIGNATURE_PATH: filePath }
|
|
20822
|
+
}
|
|
20823
|
+
);
|
|
20824
|
+
const parsed = JSON.parse(stdout.trim());
|
|
20825
|
+
const text2 = (value) => typeof value === "string" && value ? value : void 0;
|
|
20826
|
+
const status = text2(parsed.Status);
|
|
20827
|
+
return {
|
|
20828
|
+
verified: status === "Valid",
|
|
20829
|
+
status,
|
|
20830
|
+
signer: text2(parsed.Subject),
|
|
20831
|
+
issuer: text2(parsed.Issuer),
|
|
20832
|
+
thumbprint: text2(parsed.Thumbprint),
|
|
20833
|
+
notAfter: text2(parsed.NotAfter),
|
|
20834
|
+
timestamper: text2(parsed.Timestamper)
|
|
20835
|
+
};
|
|
20836
|
+
} catch (error) {
|
|
20837
|
+
warnings.push(
|
|
20838
|
+
`Authenticode verification did not complete: ${error instanceof Error ? error.message.split("\n")[0] : String(error)}`
|
|
20839
|
+
);
|
|
20840
|
+
return void 0;
|
|
20841
|
+
}
|
|
20842
|
+
}
|
|
20843
|
+
function packerWarnings(pe, warnings) {
|
|
20844
|
+
const packerNames = /^(upx\d|\.upx|\.aspack|\.adata|\.themida|\.vmp\d|\.petite|\.mpress|\.enigma\d|\.nsp\d|\.packed)/i;
|
|
20845
|
+
for (const section of pe.sections) {
|
|
20846
|
+
if (packerNames.test(section.name)) {
|
|
20847
|
+
warnings.push(`Section "${section.name}" is typical of a packer; static results may cover only the unpacker stub.`);
|
|
20848
|
+
return;
|
|
20849
|
+
}
|
|
20850
|
+
}
|
|
20851
|
+
const dense = pe.sections.filter((section) => section.executable && (section.entropy ?? 0) >= 7.4 && section.rawSize > 32 * 1024);
|
|
20852
|
+
if (dense.length > 0) {
|
|
20853
|
+
warnings.push(
|
|
20854
|
+
`Executable section "${dense[0].name}" has entropy ${dense[0].entropy}; the code may be packed or encrypted.`
|
|
20855
|
+
);
|
|
20856
|
+
}
|
|
20857
|
+
}
|
|
20858
|
+
async function inspectWindowsApp(appPath) {
|
|
20859
|
+
const warnings = [];
|
|
20860
|
+
const stat = import_node_fs6.default.statSync(appPath);
|
|
20861
|
+
let appRoot = stat.isDirectory() ? appPath : import_node_path4.default.dirname(appPath);
|
|
20862
|
+
const requestedExecutable = stat.isFile() ? import_node_path4.default.basename(appPath) : void 0;
|
|
20863
|
+
const appName = import_node_path4.default.basename(appRoot);
|
|
20864
|
+
let executables = requestedExecutable ? [requestedExecutable] : topLevelExecutables(appRoot);
|
|
20865
|
+
let ranked = rankExecutables(appRoot, executables, appName);
|
|
20866
|
+
const onlyHelpers = ranked.length > 0 && ranked.every((name) => HELPER_EXECUTABLE.test(name));
|
|
20867
|
+
if (!requestedExecutable && (ranked.length === 0 || onlyHelpers)) {
|
|
20868
|
+
const version2 = newestSquirrelVersion(appRoot);
|
|
20869
|
+
if (version2) {
|
|
20870
|
+
const nested = import_node_path4.default.join(appRoot, version2);
|
|
20871
|
+
const nestedRanked = rankExecutables(nested, topLevelExecutables(nested), appName);
|
|
20872
|
+
if (nestedRanked.length > 0) {
|
|
20873
|
+
warnings.push(`The program lives in "${version2}"; the newest versioned folder was inspected.`);
|
|
20874
|
+
appRoot = nested;
|
|
20875
|
+
executables = topLevelExecutables(nested);
|
|
20876
|
+
ranked = nestedRanked;
|
|
20877
|
+
}
|
|
20878
|
+
}
|
|
20879
|
+
}
|
|
20880
|
+
if (ranked.length === 0) {
|
|
20881
|
+
throw new Error("No Windows executable (.exe) was found at the top level of the application directory.");
|
|
20882
|
+
}
|
|
20883
|
+
const mainName = ranked[0];
|
|
20884
|
+
const mainPath = import_node_path4.default.join(appRoot, mainName);
|
|
20885
|
+
if (!isPeFile(mainPath)) {
|
|
20886
|
+
throw new Error(`"${mainName}" is not a Windows executable (the MZ signature is missing).`);
|
|
20887
|
+
}
|
|
20888
|
+
if (HELPER_EXECUTABLE.test(mainName)) {
|
|
20889
|
+
warnings.push(`Only helper-style executables were found; "${mainName}" was inspected but may not be the application itself.`);
|
|
20890
|
+
}
|
|
20891
|
+
if (ranked.length > 1) {
|
|
20892
|
+
const second = ranked[1];
|
|
20893
|
+
if (!HELPER_EXECUTABLE.test(second) && !requestedExecutable) {
|
|
20894
|
+
warnings.push(`Several executables could be the main program (${ranked.slice(0, 4).join(", ")}); "${mainName}" was chosen.`);
|
|
20895
|
+
}
|
|
20896
|
+
}
|
|
20897
|
+
const pe = inspectPeFile(mainPath);
|
|
20898
|
+
warnings.push(...pe.warnings);
|
|
20899
|
+
packerWarnings(pe, warnings);
|
|
20900
|
+
const walked = walk(appRoot);
|
|
20901
|
+
const asar = import_node_path4.default.join(appRoot, "resources", "app.asar");
|
|
20902
|
+
const unpacked = import_node_path4.default.join(appRoot, "resources", "app");
|
|
20903
|
+
let asarSize;
|
|
20904
|
+
try {
|
|
20905
|
+
asarSize = import_node_fs6.default.statSync(asar).size;
|
|
20906
|
+
} catch {
|
|
20907
|
+
}
|
|
20908
|
+
const electron = {
|
|
20909
|
+
asar: asarSize !== void 0,
|
|
20910
|
+
unpackedApp: import_node_fs6.default.existsSync(import_node_path4.default.join(unpacked, "package.json")),
|
|
20911
|
+
...asarSize !== void 0 ? { asarSizeBytes: asarSize } : {}
|
|
20912
|
+
};
|
|
20913
|
+
let topLevel = [];
|
|
20914
|
+
try {
|
|
20915
|
+
topLevel = import_node_fs6.default.readdirSync(appRoot).sort((a, b) => a.localeCompare(b)).slice(0, 200);
|
|
20916
|
+
} catch {
|
|
20917
|
+
}
|
|
20918
|
+
const signature = { embedded: pe.certificate.present };
|
|
20919
|
+
const verified = await verifyAuthenticode(mainPath, warnings);
|
|
20920
|
+
if (verified) Object.assign(signature, verified);
|
|
20921
|
+
if (!pe.certificate.present && (!verified || verified.status === "NotSigned")) {
|
|
20922
|
+
warnings.push("The main executable carries no Authenticode signature.");
|
|
20923
|
+
}
|
|
20924
|
+
const version = pe.version?.productVersion ?? pe.version?.fileVersion;
|
|
20925
|
+
return {
|
|
20926
|
+
kind: "windows-app",
|
|
20927
|
+
app: {
|
|
20928
|
+
path: appPath,
|
|
20929
|
+
name: pe.version?.productName ?? pe.version?.fileDescription ?? appName,
|
|
20930
|
+
version,
|
|
20931
|
+
publisher: pe.version?.companyName
|
|
20932
|
+
},
|
|
20933
|
+
layout: {
|
|
20934
|
+
root: relative(stat.isDirectory() ? appPath : import_node_path4.default.dirname(appPath), appRoot),
|
|
20935
|
+
topLevel,
|
|
20936
|
+
executableCount: walked.files.filter((file) => file.lower.endsWith(".exe")).length,
|
|
20937
|
+
dllCount: walked.files.filter((file) => file.lower.endsWith(".dll")).length,
|
|
20938
|
+
fileCount: walked.files.length,
|
|
20939
|
+
truncated: walked.truncated,
|
|
20940
|
+
electron
|
|
20941
|
+
},
|
|
20942
|
+
mainExecutable: {
|
|
20943
|
+
path: relative(stat.isDirectory() ? appPath : import_node_path4.default.dirname(appPath), mainPath),
|
|
20944
|
+
sizeBytes: pe.sizeBytes,
|
|
20945
|
+
candidates: ranked.slice(0, 8),
|
|
20946
|
+
pe
|
|
20947
|
+
},
|
|
20948
|
+
technology: detectTechnology(walked.files, walked.directories, electron, pe, mainPath),
|
|
20949
|
+
signature,
|
|
20950
|
+
warnings
|
|
20951
|
+
};
|
|
20952
|
+
}
|
|
20953
|
+
|
|
20077
20954
|
// src/inspectors/report.ts
|
|
20078
20955
|
function cell(value) {
|
|
20079
20956
|
return String(value ?? "\u2014").replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
|
|
@@ -20084,6 +20961,11 @@ function inspectionValue(inspection, key) {
|
|
|
20084
20961
|
function record(value) {
|
|
20085
20962
|
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
20086
20963
|
}
|
|
20964
|
+
function entitlementKeys(value) {
|
|
20965
|
+
if (typeof value !== "string" || !value.includes("<key>")) return void 0;
|
|
20966
|
+
const keys = [...value.matchAll(/<key>([^<]+)<\/key>/g)].map((match) => match[1]);
|
|
20967
|
+
return keys.length ? keys : void 0;
|
|
20968
|
+
}
|
|
20087
20969
|
function collectEvidencePaths(jobs) {
|
|
20088
20970
|
const paths = /* @__PURE__ */ new Set();
|
|
20089
20971
|
for (const job of jobs) {
|
|
@@ -20120,6 +21002,49 @@ function renderMacAppInspection(lines, inspection, warnings) {
|
|
|
20120
21002
|
`| Authority | ${cell(
|
|
20121
21003
|
Array.isArray(signature?.authority) ? signature?.authority.join(", ") : void 0
|
|
20122
21004
|
)} |`,
|
|
21005
|
+
`| Team identifier | ${cell(signature?.teamIdentifier)} |`,
|
|
21006
|
+
`| Entitlements | ${cell(
|
|
21007
|
+
(() => {
|
|
21008
|
+
const keys = entitlementKeys(signature?.entitlements);
|
|
21009
|
+
return keys ? keys.join(", ") : void 0;
|
|
21010
|
+
})()
|
|
21011
|
+
)} |`,
|
|
21012
|
+
""
|
|
21013
|
+
);
|
|
21014
|
+
if (warnings.length > 0) {
|
|
21015
|
+
lines.push("### Inspection warnings", "");
|
|
21016
|
+
for (const warning of warnings) lines.push(`- ${warning}`);
|
|
21017
|
+
lines.push("");
|
|
21018
|
+
}
|
|
21019
|
+
}
|
|
21020
|
+
function renderWindowsAppInspection(lines, inspection, warnings) {
|
|
21021
|
+
const app = record(inspection.app) ?? {};
|
|
21022
|
+
const layout = record(inspection.layout) ?? {};
|
|
21023
|
+
const electron = record(layout.electron);
|
|
21024
|
+
const main = record(inspection.mainExecutable);
|
|
21025
|
+
const pe = record(main?.pe);
|
|
21026
|
+
const imports = record(pe?.imports);
|
|
21027
|
+
const dotnet = record(pe?.dotnet);
|
|
21028
|
+
const signature = record(inspection.signature);
|
|
21029
|
+
const technology = record(inspection.technology);
|
|
21030
|
+
const hints = Array.isArray(technology?.hints) ? technology.hints.map(String) : [];
|
|
21031
|
+
lines.push(
|
|
21032
|
+
"| Field | Value |",
|
|
21033
|
+
"| --- | --- |",
|
|
21034
|
+
`| Name | ${cell(app.name)} |`,
|
|
21035
|
+
`| Version | ${cell(app.version)} |`,
|
|
21036
|
+
`| Publisher | ${cell(app.publisher)} |`,
|
|
21037
|
+
`| Main executable | ${cell(main?.path)} |`,
|
|
21038
|
+
`| Architecture | ${cell(pe?.architecture)} (${cell(pe?.format)}) |`,
|
|
21039
|
+
`| Subsystem | ${cell(pe?.subsystem)} |`,
|
|
21040
|
+
`| Imported libraries | ${cell(imports?.libraryCount)} |`,
|
|
21041
|
+
`| .NET runtime | ${cell(dotnet ? dotnet.runtimeVersion ?? "managed" : void 0)} |`,
|
|
21042
|
+
`| Executables / DLLs | ${cell(layout.executableCount)} / ${cell(layout.dllCount)} |`,
|
|
21043
|
+
`| Electron bundle | ${cell(electron?.asar ? "app.asar" : electron?.unpackedApp ? "unpacked app" : "none")} |`,
|
|
21044
|
+
`| Technology hints | ${cell(hints.length > 0 ? hints.join(", ") : void 0)} |`,
|
|
21045
|
+
`| Signature embedded | ${cell(signature?.embedded)} |`,
|
|
21046
|
+
`| Signature verified | ${cell(signature?.verified ?? "not checked on this host")} |`,
|
|
21047
|
+
`| Signer | ${cell(signature?.signer)} |`,
|
|
20123
21048
|
""
|
|
20124
21049
|
);
|
|
20125
21050
|
if (warnings.length > 0) {
|
|
@@ -20161,6 +21086,210 @@ function renderIpaInspection(lines, inspection, warnings) {
|
|
|
20161
21086
|
lines.push("");
|
|
20162
21087
|
}
|
|
20163
21088
|
}
|
|
21089
|
+
function numberValue(value) {
|
|
21090
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
21091
|
+
}
|
|
21092
|
+
var IRREGULAR_PLURALS = {
|
|
21093
|
+
"storage entry": "storage entries",
|
|
21094
|
+
slice: "slices"
|
|
21095
|
+
};
|
|
21096
|
+
function countLabel(count, noun) {
|
|
21097
|
+
const many = IRREGULAR_PLURALS[noun] ?? `${noun}s`;
|
|
21098
|
+
return `${count.toLocaleString("en-US")} ${count === 1 ? noun : many}`;
|
|
21099
|
+
}
|
|
21100
|
+
var PLATFORM_LABEL = {
|
|
21101
|
+
ios: "iOS",
|
|
21102
|
+
macos: "macOS",
|
|
21103
|
+
windows: "Windows",
|
|
21104
|
+
linux: "Linux"
|
|
21105
|
+
};
|
|
21106
|
+
var FORMAT_LABEL = {
|
|
21107
|
+
ipa: "IPA",
|
|
21108
|
+
app: "macOS app bundle",
|
|
21109
|
+
exe: "Windows executable"
|
|
21110
|
+
};
|
|
21111
|
+
function formatBytes(bytes) {
|
|
21112
|
+
if (typeof bytes !== "number" || bytes <= 0) return "unknown size";
|
|
21113
|
+
const units = ["B", "KB", "MB", "GB"];
|
|
21114
|
+
let size = bytes;
|
|
21115
|
+
let unit = 0;
|
|
21116
|
+
while (size >= 1024 && unit < units.length - 1) {
|
|
21117
|
+
size /= 1024;
|
|
21118
|
+
unit += 1;
|
|
21119
|
+
}
|
|
21120
|
+
return `${size.toFixed(unit === 0 ? 0 : 1)} ${units[unit]}`;
|
|
21121
|
+
}
|
|
21122
|
+
function routeSegments(value) {
|
|
21123
|
+
return value.replace(/^(?:\.{1,2}\/)+/, "").replace(/^\/+/, "").split(/[/?#]/).filter((segment) => segment.length > 0);
|
|
21124
|
+
}
|
|
21125
|
+
function routeGroup(value, depth = 1) {
|
|
21126
|
+
const segments = routeSegments(value).slice(0, depth);
|
|
21127
|
+
return segments.length > 0 ? segments.join("/") : "(root)";
|
|
21128
|
+
}
|
|
21129
|
+
function groupRoutes(values2) {
|
|
21130
|
+
if (values2.length === 0) return [];
|
|
21131
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
21132
|
+
for (const value of values2) {
|
|
21133
|
+
const key = routeGroup(value, 1);
|
|
21134
|
+
const bucket = buckets.get(key);
|
|
21135
|
+
if (bucket) bucket.values.push(value);
|
|
21136
|
+
else buckets.set(key, { depth: 1, values: [value] });
|
|
21137
|
+
}
|
|
21138
|
+
for (let guard = 0; guard < 8; guard += 1) {
|
|
21139
|
+
const [key, bucket] = [...buckets.entries()].sort(
|
|
21140
|
+
(a, b) => b[1].values.length - a[1].values.length
|
|
21141
|
+
)[0];
|
|
21142
|
+
if (bucket.depth >= 3 || bucket.values.length <= values2.length / 2) break;
|
|
21143
|
+
const split = /* @__PURE__ */ new Map();
|
|
21144
|
+
for (const value of bucket.values) {
|
|
21145
|
+
const next = routeGroup(value, bucket.depth + 1);
|
|
21146
|
+
const list = split.get(next);
|
|
21147
|
+
if (list) list.push(value);
|
|
21148
|
+
else split.set(next, [value]);
|
|
21149
|
+
}
|
|
21150
|
+
if (split.size * 2 > bucket.values.length) break;
|
|
21151
|
+
if (split.size === 1 && split.has(key)) break;
|
|
21152
|
+
buckets.delete(key);
|
|
21153
|
+
for (const [next, list] of split) {
|
|
21154
|
+
buckets.set(next, { depth: bucket.depth + 1, values: list });
|
|
21155
|
+
}
|
|
21156
|
+
}
|
|
21157
|
+
return [...buckets.entries()].map(([group, bucket]) => ({
|
|
21158
|
+
group,
|
|
21159
|
+
count: bucket.values.length,
|
|
21160
|
+
example: bucket.values[0]
|
|
21161
|
+
})).sort((a, b) => b.count - a.count || a.group.localeCompare(b.group));
|
|
21162
|
+
}
|
|
21163
|
+
function selectReportJobs(jobs, limit = 20) {
|
|
21164
|
+
const window = jobs.slice(0, limit);
|
|
21165
|
+
const present = new Set(window.map((job) => job.id));
|
|
21166
|
+
const carried = [];
|
|
21167
|
+
for (const capability of ["native:inspect"]) {
|
|
21168
|
+
const latest = jobs.find(
|
|
21169
|
+
(job) => job.capabilityId === capability && String(job.status) === "analyzed"
|
|
21170
|
+
);
|
|
21171
|
+
if (latest && !present.has(latest.id)) carried.push(latest);
|
|
21172
|
+
}
|
|
21173
|
+
return [...window, ...carried];
|
|
21174
|
+
}
|
|
21175
|
+
function nativeFacts(jobs) {
|
|
21176
|
+
const job = [...jobs].reverse().find(
|
|
21177
|
+
(item) => item.capabilityId === "native:inspect" && String(item.status) === "analyzed"
|
|
21178
|
+
);
|
|
21179
|
+
if (!job?.result) return { inspected: false };
|
|
21180
|
+
const counts = record(job.result.counts) ?? {};
|
|
21181
|
+
const encryption = record(job.result.encryption) ?? {};
|
|
21182
|
+
return {
|
|
21183
|
+
inspected: true,
|
|
21184
|
+
procedureCount: numberValue(
|
|
21185
|
+
counts.procedure_count ?? counts.procedureCount
|
|
21186
|
+
),
|
|
21187
|
+
stringCount: numberValue(counts.string_count ?? counts.stringCount),
|
|
21188
|
+
declaredSlices: numberValue(encryption.declaredSlices),
|
|
21189
|
+
encryptedSlices: numberValue(encryption.encryptedSlices),
|
|
21190
|
+
allDeclaredEncrypted: typeof encryption.allDeclaredEncrypted === "boolean" ? encryption.allDeclaredEncrypted : void 0
|
|
21191
|
+
};
|
|
21192
|
+
}
|
|
21193
|
+
function encryptionStatement(native, packageFormat) {
|
|
21194
|
+
if (packageFormat !== "ipa") {
|
|
21195
|
+
return "Not applicable: declared encryption only applies to iOS Mach-O slices.";
|
|
21196
|
+
}
|
|
21197
|
+
if (!native.inspected) {
|
|
21198
|
+
return "Not determined \u2014 native inspection has not run for this artifact, so the encryption state is unknown.";
|
|
21199
|
+
}
|
|
21200
|
+
const declared = native.declaredSlices ?? 0;
|
|
21201
|
+
const encrypted = native.encryptedSlices ?? 0;
|
|
21202
|
+
if (declared === 0) {
|
|
21203
|
+
return "The main executable declares no slices, so encryption state is unknown.";
|
|
21204
|
+
}
|
|
21205
|
+
if (encrypted === 0) {
|
|
21206
|
+
return `No slice declares encryption (0 of ${declared}). Native code is readable, subject to normal static-analysis limits.`;
|
|
21207
|
+
}
|
|
21208
|
+
const subject = native.allDeclaredEncrypted ? "Every main-executable slice declares" : `${encrypted} of ${countLabel(declared, "slice")} declare`;
|
|
21209
|
+
return `${subject} encryption (\`cryptid != 0\`, FairPlay). Symbol and string tables remain readable; decompiling those regions does not yield trustworthy original code.`;
|
|
21210
|
+
}
|
|
21211
|
+
function renderSummary(input2) {
|
|
21212
|
+
const { target, artifact, counts, routeGroups, native, technology } = input2;
|
|
21213
|
+
const countOf = (type) => counts.get(type) ?? 0;
|
|
21214
|
+
const lines = ["## Summary", ""];
|
|
21215
|
+
const format = artifact ? FORMAT_LABEL[artifact.packageFormat] ?? artifact.packageFormat.toUpperCase() : void 0;
|
|
21216
|
+
const platform = PLATFORM_LABEL[target.targetRef.platform] ?? target.targetRef.platform;
|
|
21217
|
+
const identity = [
|
|
21218
|
+
`**${target.name}**`,
|
|
21219
|
+
`${platform} application`,
|
|
21220
|
+
target.version ? `version ${target.version}` : void 0,
|
|
21221
|
+
target.publisher ? `published by ${target.publisher}` : void 0
|
|
21222
|
+
].filter(Boolean).join(", ");
|
|
21223
|
+
const article = format && /^[AEIOU]/i.test(format) ? "an" : "a";
|
|
21224
|
+
lines.push(
|
|
21225
|
+
`${identity}${/[.!?]$/.test(identity) ? "" : "."}${artifact ? ` The artifact is ${article} ${format} of ${formatBytes(artifact.sizeBytes)}, SHA-256 \`${artifact.sha256.slice(0, 12)}\u2026\`.` : " No artifact has been registered yet."}`,
|
|
21226
|
+
""
|
|
21227
|
+
);
|
|
21228
|
+
const hasScriptSurface = countOf("route") > 0 || countOf("module") > 0 || countOf("asset") > 0;
|
|
21229
|
+
if (hasScriptSurface) {
|
|
21230
|
+
const surface = [
|
|
21231
|
+
countOf("route") > 0 ? `${countLabel(countOf("route"), "route")} in ${countLabel(routeGroups.length, "path group")}` : void 0,
|
|
21232
|
+
countOf("module") > 0 ? countLabel(countOf("module"), "JavaScript module") : void 0,
|
|
21233
|
+
countOf("asset") > 0 ? countLabel(countOf("asset"), "asset") : void 0,
|
|
21234
|
+
countLabel(countOf("network_endpoint"), "network endpoint candidate"),
|
|
21235
|
+
countOf("storage") > 0 ? countLabel(countOf("storage"), "storage entry") : void 0
|
|
21236
|
+
].filter(Boolean);
|
|
21237
|
+
lines.push(`**Static surface.** ${surface.join(", ")}.`, "");
|
|
21238
|
+
if (countOf("network_endpoint") === 0) {
|
|
21239
|
+
lines.push(
|
|
21240
|
+
"No endpoint was classified. The analyzer reports absolute URLs found in inert JavaScript; a base URL assembled at runtime is not visible this way, so this is **not** evidence that the app makes no network calls.",
|
|
21241
|
+
""
|
|
21242
|
+
);
|
|
21243
|
+
}
|
|
21244
|
+
} else {
|
|
21245
|
+
lines.push(
|
|
21246
|
+
"**Static surface.** No bundle script, route or module findings were recorded; the bundle inspection above is the complete recorded surface.",
|
|
21247
|
+
""
|
|
21248
|
+
);
|
|
21249
|
+
}
|
|
21250
|
+
lines.push(
|
|
21251
|
+
`**Technology signals.** ${technology.length > 0 ? technology.join(" / ") : "none detected"} (static hints, not a build-tool claim).`,
|
|
21252
|
+
"",
|
|
21253
|
+
`**Encryption.** ${encryptionStatement(native, artifact?.packageFormat)}`,
|
|
21254
|
+
""
|
|
21255
|
+
);
|
|
21256
|
+
if (native.inspected) {
|
|
21257
|
+
const indexed = [
|
|
21258
|
+
native.procedureCount !== void 0 ? countLabel(native.procedureCount, "procedure") : void 0,
|
|
21259
|
+
native.stringCount !== void 0 ? countLabel(native.stringCount, "string") : void 0
|
|
21260
|
+
].filter(Boolean);
|
|
21261
|
+
if (indexed.length > 0) {
|
|
21262
|
+
lines.push(
|
|
21263
|
+
`**Native surface.** ${indexed.join(" and ")} indexed; \`search_native\` and \`decompile_function\` answer from this index.`,
|
|
21264
|
+
""
|
|
21265
|
+
);
|
|
21266
|
+
}
|
|
21267
|
+
}
|
|
21268
|
+
const questions = [];
|
|
21269
|
+
if (countOf("route") > 0 || countOf("module") > 0) {
|
|
21270
|
+
questions.push(
|
|
21271
|
+
"Which routes and modules make up a feature, and where each one is defined in the bundle."
|
|
21272
|
+
);
|
|
21273
|
+
}
|
|
21274
|
+
if (native.inspected) {
|
|
21275
|
+
questions.push(
|
|
21276
|
+
"Which native symbols and strings match a name, and what one exact symbol decompiles to."
|
|
21277
|
+
);
|
|
21278
|
+
}
|
|
21279
|
+
if (technology.length > 0) {
|
|
21280
|
+
questions.push("Which framework produced the package and how it is laid out.");
|
|
21281
|
+
}
|
|
21282
|
+
if (questions.length > 0) {
|
|
21283
|
+
lines.push("**Questions this evidence can answer.**", "");
|
|
21284
|
+
for (const question of questions) lines.push(`- ${question}`);
|
|
21285
|
+
lines.push("");
|
|
21286
|
+
}
|
|
21287
|
+
lines.push(
|
|
21288
|
+
"**What it cannot establish.** Runtime behaviour, server-side logic, and code paths outside the recorded evidence; the app was not executed and no backend was contacted.",
|
|
21289
|
+
""
|
|
21290
|
+
);
|
|
21291
|
+
return lines;
|
|
21292
|
+
}
|
|
20164
21293
|
function renderReportMarkdown(input2) {
|
|
20165
21294
|
const {
|
|
20166
21295
|
target,
|
|
@@ -20174,6 +21303,20 @@ function renderReportMarkdown(input2) {
|
|
|
20174
21303
|
const inspection = artifact?.inspection;
|
|
20175
21304
|
const warnings = Array.isArray(inspection?.warnings) ? inspection.warnings.map(String) : [];
|
|
20176
21305
|
const evidencePaths = collectEvidencePaths(jobs);
|
|
21306
|
+
const counts = /* @__PURE__ */ new Map();
|
|
21307
|
+
for (const [type, total] of Object.entries(input2.typeTotals ?? {})) {
|
|
21308
|
+
counts.set(type, total);
|
|
21309
|
+
}
|
|
21310
|
+
if (counts.size === 0) {
|
|
21311
|
+
for (const finding of findings) {
|
|
21312
|
+
counts.set(finding.type, (counts.get(finding.type) ?? 0) + 1);
|
|
21313
|
+
}
|
|
21314
|
+
}
|
|
21315
|
+
const technology = findings.filter((finding) => finding.type === "technology").map((finding) => finding.value);
|
|
21316
|
+
const routeGroups = groupRoutes(
|
|
21317
|
+
findings.filter((finding) => finding.type === "route").map((finding) => finding.value)
|
|
21318
|
+
);
|
|
21319
|
+
const native = nativeFacts(jobs);
|
|
20177
21320
|
const lines = [
|
|
20178
21321
|
`# ${target.name} \xB7 Analysis report`,
|
|
20179
21322
|
"",
|
|
@@ -20183,6 +21326,14 @@ function renderReportMarkdown(input2) {
|
|
|
20183
21326
|
`- Source: ${target.targetRef.sourceId} / ${target.targetRef.sourceRef}`,
|
|
20184
21327
|
"- Scope: this report summarizes the local artifact, static inspection and job records. It does not describe runtime behavior.",
|
|
20185
21328
|
"",
|
|
21329
|
+
...renderSummary({
|
|
21330
|
+
target,
|
|
21331
|
+
artifact,
|
|
21332
|
+
counts,
|
|
21333
|
+
routeGroups,
|
|
21334
|
+
native,
|
|
21335
|
+
technology
|
|
21336
|
+
}),
|
|
20186
21337
|
"## Target",
|
|
20187
21338
|
"",
|
|
20188
21339
|
"| Field | Value |",
|
|
@@ -20219,6 +21370,8 @@ function renderReportMarkdown(input2) {
|
|
|
20219
21370
|
lines.push(`Inspection failed: ${cell(inspection.error)}`, "");
|
|
20220
21371
|
} else if (inspection.kind === "mac-app") {
|
|
20221
21372
|
renderMacAppInspection(lines, inspection, warnings);
|
|
21373
|
+
} else if (inspection.kind === "windows-app") {
|
|
21374
|
+
renderWindowsAppInspection(lines, inspection, warnings);
|
|
20222
21375
|
} else {
|
|
20223
21376
|
renderIpaInspection(lines, inspection, warnings);
|
|
20224
21377
|
}
|
|
@@ -20231,34 +21384,71 @@ function renderReportMarkdown(input2) {
|
|
|
20231
21384
|
"| Type | Count |",
|
|
20232
21385
|
"| --- | --- |"
|
|
20233
21386
|
);
|
|
20234
|
-
|
|
20235
|
-
for (const finding of findings) {
|
|
20236
|
-
byType.set(finding.type, (byType.get(finding.type) ?? 0) + 1);
|
|
20237
|
-
}
|
|
20238
|
-
if (byType.size === 0) {
|
|
21387
|
+
if (counts.size === 0) {
|
|
20239
21388
|
lines.push("| \u2014 | 0 |");
|
|
20240
21389
|
} else {
|
|
20241
|
-
for (const [type, count] of [...
|
|
21390
|
+
for (const [type, count] of [...counts.entries()].sort(
|
|
20242
21391
|
(a, b) => a[0].localeCompare(b[0])
|
|
20243
21392
|
)) {
|
|
20244
21393
|
lines.push(`| ${cell(type)} | ${count} |`);
|
|
20245
21394
|
}
|
|
20246
21395
|
}
|
|
20247
21396
|
lines.push("");
|
|
21397
|
+
const itemLine = (item) => {
|
|
21398
|
+
const location = item.sourcePath ? ` \u2014 \`${item.sourcePath}\`${item.start ? `:${item.start.line}:${item.start.column}` : ""}` : "";
|
|
21399
|
+
return `- ${item.value}${location} (Evidence: \`${item.evidenceId}\`)`;
|
|
21400
|
+
};
|
|
21401
|
+
const routeItems = findings.filter((finding) => finding.type === "route");
|
|
21402
|
+
const routeTotal = counts.get("route") ?? routeItems.length;
|
|
21403
|
+
if (routeItems.length > 0) {
|
|
21404
|
+
lines.push(
|
|
21405
|
+
`### Routes (${routeTotal}, ${routeGroups.length} path groups)`,
|
|
21406
|
+
"",
|
|
21407
|
+
"Grouped by path prefix. A group holding more than half of the routes is split further, so one row cannot hide the rest.",
|
|
21408
|
+
"",
|
|
21409
|
+
"| Group | Routes | Example |",
|
|
21410
|
+
"| --- | --- | --- |"
|
|
21411
|
+
);
|
|
21412
|
+
const shown = routeGroups.slice(0, 25);
|
|
21413
|
+
for (const group of shown) {
|
|
21414
|
+
lines.push(
|
|
21415
|
+
`| ${cell(group.group)} | ${group.count} | \`${cell(group.example)}\` |`
|
|
21416
|
+
);
|
|
21417
|
+
}
|
|
21418
|
+
if (routeGroups.length > shown.length) {
|
|
21419
|
+
const rest = routeGroups.slice(shown.length).reduce((total, group) => total + group.count, 0);
|
|
21420
|
+
lines.push(
|
|
21421
|
+
`| (other ${routeGroups.length - shown.length} groups) | ${rest} | \u2014 |`
|
|
21422
|
+
);
|
|
21423
|
+
}
|
|
21424
|
+
lines.push(
|
|
21425
|
+
"",
|
|
21426
|
+
routeItems.length < routeTotal ? `The inventory below covers the ${routeItems.length} routed findings included in this report; the full set stays queryable with \`search_findings --types route\`.` : "The inventory below is complete for this artifact.",
|
|
21427
|
+
"",
|
|
21428
|
+
`<details><summary>All ${routeItems.length} routed findings</summary>`,
|
|
21429
|
+
""
|
|
21430
|
+
);
|
|
21431
|
+
for (const item of routeItems) lines.push(itemLine(item));
|
|
21432
|
+
lines.push("", "</details>", "");
|
|
21433
|
+
}
|
|
21434
|
+
const endpointItems = findings.filter(
|
|
21435
|
+
(finding) => finding.type === "network_endpoint"
|
|
21436
|
+
);
|
|
21437
|
+
if (endpointItems.length > 0) {
|
|
21438
|
+
lines.push("### Network endpoint candidates", "");
|
|
21439
|
+
for (const item of endpointItems) lines.push(itemLine(item));
|
|
21440
|
+
lines.push("");
|
|
21441
|
+
}
|
|
20248
21442
|
for (const [type, title] of [
|
|
20249
|
-
["route", "Routes"],
|
|
20250
|
-
["network_endpoint", "Network endpoint candidates"],
|
|
20251
21443
|
["module", "JavaScript modules"],
|
|
21444
|
+
["storage", "Storage entries"],
|
|
20252
21445
|
["native_procedure", "Native symbols"],
|
|
20253
21446
|
["risk", "Risks and limitations"]
|
|
20254
21447
|
]) {
|
|
20255
|
-
const items = findings.filter((finding) => finding.type === type).slice(0,
|
|
21448
|
+
const items = findings.filter((finding) => finding.type === type).slice(0, 40);
|
|
20256
21449
|
if (items.length === 0) continue;
|
|
20257
21450
|
lines.push(`### ${title}`, "");
|
|
20258
|
-
for (const item of items)
|
|
20259
|
-
const location = item.sourcePath ? ` \u2014 \`${item.sourcePath}\`${item.start ? `:${item.start.line}:${item.start.column}` : ""}` : "";
|
|
20260
|
-
lines.push(`- ${item.value}${location} (Evidence: \`${item.evidenceId}\`)`);
|
|
20261
|
-
}
|
|
21451
|
+
for (const item of items) lines.push(itemLine(item));
|
|
20262
21452
|
lines.push("");
|
|
20263
21453
|
}
|
|
20264
21454
|
const analysisGaps = findings.filter((finding) => finding.type === "limitation").slice(0, 20);
|
|
@@ -20311,8 +21501,8 @@ function describeResult(result) {
|
|
|
20311
21501
|
}
|
|
20312
21502
|
|
|
20313
21503
|
// src/process/run.ts
|
|
20314
|
-
var
|
|
20315
|
-
var
|
|
21504
|
+
var import_node_child_process3 = require("child_process");
|
|
21505
|
+
var import_node_path5 = __toESM(require("path"));
|
|
20316
21506
|
function quoteWindowsArgument(value) {
|
|
20317
21507
|
if (value.length === 0) return '""';
|
|
20318
21508
|
if (!/[\s"&|<>^()%!]/.test(value)) return value;
|
|
@@ -20322,7 +21512,7 @@ function windowsCommandLine(command, args) {
|
|
|
20322
21512
|
return [command, ...args].map(quoteWindowsArgument).join(" ");
|
|
20323
21513
|
}
|
|
20324
21514
|
function windowsSpawnSpec(command, args) {
|
|
20325
|
-
const extension =
|
|
21515
|
+
const extension = import_node_path5.default.extname(command).toLowerCase();
|
|
20326
21516
|
if (extension !== ".cmd" && extension !== ".bat") {
|
|
20327
21517
|
return { command, args, verbatim: false };
|
|
20328
21518
|
}
|
|
@@ -20340,7 +21530,7 @@ function runCommand(command, args, options = {}) {
|
|
|
20340
21530
|
let child;
|
|
20341
21531
|
try {
|
|
20342
21532
|
const spec = process.platform === "win32" ? windowsSpawnSpec(command, args) : { command, args, verbatim: false };
|
|
20343
|
-
child = (0,
|
|
21533
|
+
child = (0, import_node_child_process3.spawn)(spec.command, spec.args, {
|
|
20344
21534
|
env: options.env ?? process.env,
|
|
20345
21535
|
cwd: options.cwd,
|
|
20346
21536
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -20464,7 +21654,7 @@ function uniqueStrings(values2, limit) {
|
|
|
20464
21654
|
function isNetworkEndpointValue(value) {
|
|
20465
21655
|
return /^(?:https?|wss?):\/\//i.test(value) || /^\/\//.test(value) || /^[a-z0-9.-]+\.[a-z]{2,}(?:[/:?#]|$)/i.test(value) || /^\/[a-z0-9._~!$&'()*+,;=:@%/-]+$/i.test(value);
|
|
20466
21656
|
}
|
|
20467
|
-
function
|
|
21657
|
+
function detectTechnology2(subjectName, modulePaths, assetPaths) {
|
|
20468
21658
|
const text2 = [subjectName, ...modulePaths, ...assetPaths].filter((value) => typeof value === "string").join("\n").toLowerCase();
|
|
20469
21659
|
const hints = [];
|
|
20470
21660
|
if (/__uni__|uni-app|uni-jsframework|hbuilder/.test(text2)) {
|
|
@@ -20516,7 +21706,7 @@ function javascriptFindings(job, artifact, evidenceId, parsed, summary) {
|
|
|
20516
21706
|
}
|
|
20517
21707
|
}
|
|
20518
21708
|
}
|
|
20519
|
-
if (artifact.packageFormat === "app") {
|
|
21709
|
+
if (artifact.packageFormat === "app" || artifact.packageFormat === "exe") {
|
|
20520
21710
|
push("technology", "Electron", {
|
|
20521
21711
|
group: "technology",
|
|
20522
21712
|
confidence: "high",
|
|
@@ -20613,10 +21803,10 @@ function javascriptFindings(job, artifact, evidenceId, parsed, summary) {
|
|
|
20613
21803
|
return findings;
|
|
20614
21804
|
}
|
|
20615
21805
|
function findElectronJavascriptRoot(appPath) {
|
|
20616
|
-
const asar =
|
|
20617
|
-
if (
|
|
20618
|
-
const unpacked =
|
|
20619
|
-
if (
|
|
21806
|
+
const asar = import_node_path6.default.join(appPath, "Contents", "Resources", "app.asar");
|
|
21807
|
+
if (import_node_fs7.default.existsSync(asar)) return asar;
|
|
21808
|
+
const unpacked = import_node_path6.default.join(appPath, "Contents", "Resources", "app");
|
|
21809
|
+
if (import_node_fs7.default.existsSync(unpacked) && import_node_fs7.default.statSync(unpacked).isDirectory()) {
|
|
20620
21810
|
return unpacked;
|
|
20621
21811
|
}
|
|
20622
21812
|
fail(
|
|
@@ -20712,7 +21902,7 @@ function summarizeRea(parsed) {
|
|
|
20712
21902
|
operation: parsed.operation,
|
|
20713
21903
|
technology: {
|
|
20714
21904
|
confidence: "static-hint",
|
|
20715
|
-
hints:
|
|
21905
|
+
hints: detectTechnology2(subject?.name, modulePaths, assetPaths)
|
|
20716
21906
|
},
|
|
20717
21907
|
subject: subject ? {
|
|
20718
21908
|
name: subject.name,
|
|
@@ -20885,6 +22075,9 @@ var CapabilityJobRunner = class {
|
|
|
20885
22075
|
case "inspect:mac-app":
|
|
20886
22076
|
await this.runInspectMacApp(job, target, artifact);
|
|
20887
22077
|
return;
|
|
22078
|
+
case "inspect:pe":
|
|
22079
|
+
await this.runInspectPe(job, target, artifact);
|
|
22080
|
+
return;
|
|
20888
22081
|
case "extract:app":
|
|
20889
22082
|
await this.runExtract(job, target, artifact);
|
|
20890
22083
|
return;
|
|
@@ -20975,10 +22168,36 @@ var CapabilityJobRunner = class {
|
|
|
20975
22168
|
warnings: inspection.warnings.length
|
|
20976
22169
|
});
|
|
20977
22170
|
}
|
|
22171
|
+
async runInspectPe(job, target, artifact) {
|
|
22172
|
+
if (!artifact) fail("ARTIFACT_REQUIRED", "Acquire an artifact first.");
|
|
22173
|
+
this.log(job, "info", "Inspecting the Windows application.");
|
|
22174
|
+
this.patch(job, { progress: 25 });
|
|
22175
|
+
const inspection = await inspectWindowsApp(
|
|
22176
|
+
this.workspace.resolveStoredPath(artifact.path)
|
|
22177
|
+
);
|
|
22178
|
+
const pe = inspection.mainExecutable?.pe;
|
|
22179
|
+
const updated = {
|
|
22180
|
+
...artifact,
|
|
22181
|
+
version: inspection.app.version ?? artifact.version,
|
|
22182
|
+
architecture: pe?.architecture ?? artifact.architecture,
|
|
22183
|
+
inspection
|
|
22184
|
+
};
|
|
22185
|
+
this.store.insertArtifact(updated);
|
|
22186
|
+
this.workspace.saveArtifactMetadata(target, updated);
|
|
22187
|
+
this.patch(job, {
|
|
22188
|
+
status: "analyzed",
|
|
22189
|
+
progress: 100,
|
|
22190
|
+
result: { inspection },
|
|
22191
|
+
finishedAt: now()
|
|
22192
|
+
});
|
|
22193
|
+
this.log(job, "info", "Windows application inspection complete.", {
|
|
22194
|
+
warnings: inspection.warnings.length
|
|
22195
|
+
});
|
|
22196
|
+
}
|
|
20978
22197
|
async runExtract(job, target, artifact) {
|
|
20979
22198
|
if (!artifact) fail("ARTIFACT_REQUIRED", "\u8BF7\u5148\u83B7\u53D6 artifact\u3002");
|
|
20980
22199
|
this.log(job, "info", "\u5F00\u59CB\u89E3\u5305 .app\u3002");
|
|
20981
|
-
const outputDir =
|
|
22200
|
+
const outputDir = import_node_path6.default.join(
|
|
20982
22201
|
this.workspace.analysisDir(target),
|
|
20983
22202
|
"extract-app",
|
|
20984
22203
|
job.id
|
|
@@ -20992,7 +22211,7 @@ var CapabilityJobRunner = class {
|
|
|
20992
22211
|
progress: 100,
|
|
20993
22212
|
result: {
|
|
20994
22213
|
...extracted,
|
|
20995
|
-
outputDir:
|
|
22214
|
+
outputDir: import_node_path6.default.relative(this.workspace.root, extracted.outputDir)
|
|
20996
22215
|
},
|
|
20997
22216
|
finishedAt: now()
|
|
20998
22217
|
});
|
|
@@ -21003,19 +22222,34 @@ var CapabilityJobRunner = class {
|
|
|
21003
22222
|
async runReaJavascript(job, target, artifact) {
|
|
21004
22223
|
if (!artifact) fail("ARTIFACT_REQUIRED", "\u8BF7\u5148\u83B7\u53D6 artifact\u3002");
|
|
21005
22224
|
let javascriptRoot;
|
|
21006
|
-
if (artifact.packageFormat === "
|
|
22225
|
+
if (artifact.packageFormat === "exe") {
|
|
22226
|
+
const found = findWindowsElectronRoot(
|
|
22227
|
+
this.workspace.resolveStoredPath(artifact.path)
|
|
22228
|
+
);
|
|
22229
|
+
if (!found) {
|
|
22230
|
+
fail(
|
|
22231
|
+
"CAPABILITY_UNAVAILABLE",
|
|
22232
|
+
"No Electron JavaScript bundle (resources/app.asar or resources/app) was found in this application."
|
|
22233
|
+
);
|
|
22234
|
+
}
|
|
22235
|
+
javascriptRoot = found;
|
|
22236
|
+
this.log(job, "info", "Running static analysis on the Windows JavaScript bundle.", {
|
|
22237
|
+
path: import_node_path6.default.relative(this.workspace.root, javascriptRoot)
|
|
22238
|
+
});
|
|
22239
|
+
this.patch(job, { progress: 25 });
|
|
22240
|
+
} else if (artifact.packageFormat === "app") {
|
|
21007
22241
|
javascriptRoot = findElectronJavascriptRoot(
|
|
21008
22242
|
this.workspace.resolveStoredPath(artifact.path)
|
|
21009
22243
|
);
|
|
21010
22244
|
this.log(job, "info", "Running static analysis on the macOS JavaScript bundle.", {
|
|
21011
|
-
path:
|
|
22245
|
+
path: import_node_path6.default.relative(this.workspace.root, javascriptRoot)
|
|
21012
22246
|
});
|
|
21013
22247
|
this.patch(job, { progress: 25 });
|
|
21014
22248
|
} else {
|
|
21015
22249
|
const extraction = await this.ensureExtraction(job, target, artifact);
|
|
21016
22250
|
javascriptRoot = extraction.appRoot;
|
|
21017
22251
|
this.log(job, "info", "\u5BF9\u89E3\u5305\u76EE\u5F55\u8FD0\u884C JavaScript \u9759\u6001\u5206\u6790\u3002", {
|
|
21018
|
-
path:
|
|
22252
|
+
path: import_node_path6.default.relative(this.workspace.root, javascriptRoot)
|
|
21019
22253
|
});
|
|
21020
22254
|
this.patch(job, { progress: 25 });
|
|
21021
22255
|
}
|
|
@@ -21027,7 +22261,11 @@ var CapabilityJobRunner = class {
|
|
|
21027
22261
|
"analyze-javascript-application",
|
|
21028
22262
|
javascriptRoot,
|
|
21029
22263
|
"--analysis-timeout-ms",
|
|
21030
|
-
"300000"
|
|
22264
|
+
"300000",
|
|
22265
|
+
// Signed Electron apps often carry unpacked native binaries whose
|
|
22266
|
+
// bytes no longer match the integrity recorded in app.asar. Keep
|
|
22267
|
+
// going and let REA mark those files untrusted rather than abort.
|
|
22268
|
+
...artifact.packageFormat === "ipa" ? [] : ["--integrity-policy", "record-and-continue"]
|
|
21031
22269
|
],
|
|
21032
22270
|
// Electron bundles are much larger than iOS web bundles; a 136 MB asar
|
|
21033
22271
|
// takes several minutes on its own.
|
|
@@ -21035,7 +22273,7 @@ var CapabilityJobRunner = class {
|
|
|
21035
22273
|
);
|
|
21036
22274
|
const evidenceId = this.reaEvidenceId(rea);
|
|
21037
22275
|
const summary = { ...rea.summary };
|
|
21038
|
-
if (artifact.packageFormat === "app") {
|
|
22276
|
+
if (artifact.packageFormat === "app" || artifact.packageFormat === "exe") {
|
|
21039
22277
|
const technology = record2(summary.technology) ?? {};
|
|
21040
22278
|
const hints = Array.isArray(technology.hints) ? [...technology.hints] : [];
|
|
21041
22279
|
if (!hints.includes("Electron")) hints.unshift("Electron");
|
|
@@ -21064,7 +22302,7 @@ var CapabilityJobRunner = class {
|
|
|
21064
22302
|
result: {
|
|
21065
22303
|
...summary,
|
|
21066
22304
|
evidenceId,
|
|
21067
|
-
outputDir:
|
|
22305
|
+
outputDir: import_node_path6.default.relative(this.workspace.root, javascriptRoot),
|
|
21068
22306
|
evidencePath: rea.rawPath
|
|
21069
22307
|
},
|
|
21070
22308
|
finishedAt: now()
|
|
@@ -21108,7 +22346,7 @@ var CapabilityJobRunner = class {
|
|
|
21108
22346
|
result: {
|
|
21109
22347
|
...rea.summary,
|
|
21110
22348
|
evidenceId,
|
|
21111
|
-
binaryPath:
|
|
22349
|
+
binaryPath: import_node_path6.default.relative(this.workspace.root, prepared.binaryPath),
|
|
21112
22350
|
evidencePath: rea.rawPath,
|
|
21113
22351
|
encryption,
|
|
21114
22352
|
warning: encryption.encryptedSlices > 0 ? "\u4E3B\u7A0B\u5E8F\u5305\u542B\u58F0\u660E\u52A0\u5BC6\u7684\u5207\u7247\uFF1B\u4E0D\u8981\u628A\u52A0\u5BC6\u533A\u53CD\u7F16\u8BD1\u7ED3\u679C\u5F53\u4F5C\u53EF\u4FE1\u539F\u59CB\u5B9E\u73B0\u3002" : void 0
|
|
@@ -21171,7 +22409,7 @@ var CapabilityJobRunner = class {
|
|
|
21171
22409
|
mode,
|
|
21172
22410
|
matchCount: rawMatches.length,
|
|
21173
22411
|
matches,
|
|
21174
|
-
binaryPath:
|
|
22412
|
+
binaryPath: import_node_path6.default.relative(this.workspace.root, prepared.binaryPath),
|
|
21175
22413
|
evidencePath: rea.rawPath,
|
|
21176
22414
|
limitations: Array.isArray(rea.parsed.limitations) ? rea.parsed.limitations : [],
|
|
21177
22415
|
encryption: encryptionState(prepared.inspection)
|
|
@@ -21216,7 +22454,7 @@ var CapabilityJobRunner = class {
|
|
|
21216
22454
|
result: {
|
|
21217
22455
|
...rea.summary,
|
|
21218
22456
|
functionName: input2.functionName,
|
|
21219
|
-
binaryPath:
|
|
22457
|
+
binaryPath: import_node_path6.default.relative(this.workspace.root, prepared.binaryPath),
|
|
21220
22458
|
evidencePath: rea.rawPath,
|
|
21221
22459
|
encryption
|
|
21222
22460
|
},
|
|
@@ -21231,7 +22469,7 @@ var CapabilityJobRunner = class {
|
|
|
21231
22469
|
this.log(job, "info", "\u6C47\u603B Target\u3001artifact \u548C\u4EFB\u52A1\u8BC1\u636E\u3002");
|
|
21232
22470
|
this.patch(job, { progress: 35 });
|
|
21233
22471
|
const generatedAt = now();
|
|
21234
|
-
const reportPath =
|
|
22472
|
+
const reportPath = import_node_path6.default.join(
|
|
21235
22473
|
this.workspace.targetDir(target),
|
|
21236
22474
|
"reports",
|
|
21237
22475
|
`${job.id}.md`
|
|
@@ -21247,13 +22485,22 @@ var CapabilityJobRunner = class {
|
|
|
21247
22485
|
"risk",
|
|
21248
22486
|
"limitation"
|
|
21249
22487
|
];
|
|
22488
|
+
const limits = { route: 200, module: 200 };
|
|
21250
22489
|
const findings = findingTypes.flatMap(
|
|
21251
22490
|
(type) => this.store.searchFindings({
|
|
21252
22491
|
targetId: target.id,
|
|
21253
22492
|
types: [type],
|
|
21254
|
-
limit:
|
|
22493
|
+
limit: limits[type] ?? 40
|
|
21255
22494
|
}).items
|
|
21256
22495
|
);
|
|
22496
|
+
const typeTotals = {};
|
|
22497
|
+
for (const type of findingTypes) {
|
|
22498
|
+
typeTotals[type] = this.store.searchFindings({
|
|
22499
|
+
targetId: target.id,
|
|
22500
|
+
types: [type],
|
|
22501
|
+
limit: 1
|
|
22502
|
+
}).total;
|
|
22503
|
+
}
|
|
21257
22504
|
const findingTotal = this.store.searchFindings({
|
|
21258
22505
|
targetId: target.id,
|
|
21259
22506
|
limit: 1
|
|
@@ -21261,14 +22508,18 @@ var CapabilityJobRunner = class {
|
|
|
21261
22508
|
const markdown = renderReportMarkdown({
|
|
21262
22509
|
target,
|
|
21263
22510
|
artifact,
|
|
21264
|
-
jobs:
|
|
22511
|
+
jobs: selectReportJobs(
|
|
22512
|
+
this.store.listJobs(target.id).filter((item) => item.id !== job.id),
|
|
22513
|
+
20
|
|
22514
|
+
),
|
|
21265
22515
|
findings,
|
|
21266
22516
|
findingTotal,
|
|
22517
|
+
typeTotals,
|
|
21267
22518
|
hostPlatform: this.config.hostPlatform,
|
|
21268
22519
|
generatedAt
|
|
21269
22520
|
});
|
|
21270
22521
|
this.workspace.writeText(reportPath, markdown);
|
|
21271
|
-
const relativePath =
|
|
22522
|
+
const relativePath = import_node_path6.default.relative(this.workspace.root, reportPath);
|
|
21272
22523
|
this.patch(job, {
|
|
21273
22524
|
status: "analyzed",
|
|
21274
22525
|
progress: 100,
|
|
@@ -21300,12 +22551,12 @@ var CapabilityJobRunner = class {
|
|
|
21300
22551
|
fail("MAIN_EXECUTABLE_NOT_FOUND", "IPA \u68C0\u67E5\u7ED3\u679C\u4E2D\u6CA1\u6709\u4E3B\u7A0B\u5E8F\u8DEF\u5F84\u3002");
|
|
21301
22552
|
}
|
|
21302
22553
|
const extraction = await this.ensureExtraction(job, target, current);
|
|
21303
|
-
const binaryPath =
|
|
21304
|
-
const root =
|
|
21305
|
-
if (binaryPath !== root && !binaryPath.startsWith(root +
|
|
22554
|
+
const binaryPath = import_node_path6.default.resolve(extraction.outputDir, executable);
|
|
22555
|
+
const root = import_node_path6.default.resolve(extraction.outputDir);
|
|
22556
|
+
if (binaryPath !== root && !binaryPath.startsWith(root + import_node_path6.default.sep)) {
|
|
21306
22557
|
fail("UNSAFE_ARTIFACT_PATH", "\u4E3B\u7A0B\u5E8F\u8DEF\u5F84\u8D8A\u51FA\u89E3\u5305\u76EE\u5F55\uFF0C\u5DF2\u62D2\u7EDD\u6267\u884C\u3002");
|
|
21307
22558
|
}
|
|
21308
|
-
if (!
|
|
22559
|
+
if (!import_node_fs7.default.existsSync(binaryPath)) {
|
|
21309
22560
|
fail("MAIN_EXECUTABLE_NOT_FOUND", `\u89E3\u5305\u76EE\u5F55\u4E2D\u6CA1\u6709\u4E3B\u7A0B\u5E8F\uFF1A${executable}`);
|
|
21310
22561
|
}
|
|
21311
22562
|
return { inspection, binaryPath };
|
|
@@ -21314,24 +22565,24 @@ var CapabilityJobRunner = class {
|
|
|
21314
22565
|
const previous = this.findExtraction(target.id, artifact.id);
|
|
21315
22566
|
if (previous) {
|
|
21316
22567
|
this.log(job, "info", "\u590D\u7528\u5DF2\u6709\u89E3\u5305\u76EE\u5F55\u3002", {
|
|
21317
|
-
path:
|
|
22568
|
+
path: import_node_path6.default.relative(this.workspace.root, previous.outputDir)
|
|
21318
22569
|
});
|
|
21319
22570
|
return previous;
|
|
21320
22571
|
}
|
|
21321
|
-
const outputDir =
|
|
22572
|
+
const outputDir = import_node_path6.default.join(
|
|
21322
22573
|
this.workspace.analysisDir(target),
|
|
21323
22574
|
"extracted",
|
|
21324
22575
|
artifact.id
|
|
21325
22576
|
);
|
|
21326
|
-
const existingApp =
|
|
22577
|
+
const existingApp = import_node_fs7.default.existsSync(outputDir) ? findAppPrefix(outputDir) : void 0;
|
|
21327
22578
|
if (existingApp) {
|
|
21328
22579
|
const location = {
|
|
21329
22580
|
outputDir,
|
|
21330
22581
|
prefix: existingApp,
|
|
21331
|
-
appRoot:
|
|
22582
|
+
appRoot: import_node_path6.default.join(outputDir, existingApp)
|
|
21332
22583
|
};
|
|
21333
22584
|
this.log(job, "info", "\u590D\u7528\u6309 artifact \u54C8\u5E0C\u5F52\u6863\u7684\u89E3\u5305\u76EE\u5F55\u3002", {
|
|
21334
|
-
path:
|
|
22585
|
+
path: import_node_path6.default.relative(this.workspace.root, outputDir)
|
|
21335
22586
|
});
|
|
21336
22587
|
return location;
|
|
21337
22588
|
}
|
|
@@ -21341,8 +22592,8 @@ var CapabilityJobRunner = class {
|
|
|
21341
22592
|
this.workspace.resolveStoredPath(artifact.path),
|
|
21342
22593
|
outputDir
|
|
21343
22594
|
);
|
|
21344
|
-
const appRoot =
|
|
21345
|
-
if (appRoot !== outputDir && !appRoot.startsWith(outputDir +
|
|
22595
|
+
const appRoot = import_node_path6.default.resolve(outputDir, extracted.prefix);
|
|
22596
|
+
if (appRoot !== outputDir && !appRoot.startsWith(outputDir + import_node_path6.default.sep)) {
|
|
21346
22597
|
fail("UNSAFE_ARTIFACT_PATH", "\u89E3\u5305\u524D\u7F00\u8D8A\u51FA\u8F93\u51FA\u76EE\u5F55\uFF0C\u5DF2\u62D2\u7EDD\u7EE7\u7EED\u3002");
|
|
21347
22598
|
}
|
|
21348
22599
|
return { outputDir, appRoot, prefix: extracted.prefix };
|
|
@@ -21357,8 +22608,8 @@ var CapabilityJobRunner = class {
|
|
|
21357
22608
|
if (typeof output !== "string" || typeof prefix !== "string") continue;
|
|
21358
22609
|
try {
|
|
21359
22610
|
const outputDir = this.workspace.resolveStoredPath(output);
|
|
21360
|
-
const appRoot =
|
|
21361
|
-
if (
|
|
22611
|
+
const appRoot = import_node_path6.default.resolve(outputDir, prefix);
|
|
22612
|
+
if (import_node_fs7.default.existsSync(appRoot) && (appRoot === outputDir || appRoot.startsWith(outputDir + import_node_path6.default.sep))) {
|
|
21362
22613
|
return { outputDir, appRoot, prefix };
|
|
21363
22614
|
}
|
|
21364
22615
|
} catch {
|
|
@@ -21367,11 +22618,11 @@ var CapabilityJobRunner = class {
|
|
|
21367
22618
|
return void 0;
|
|
21368
22619
|
}
|
|
21369
22620
|
async runReaJson(job, target, folder, args, timeoutMs) {
|
|
21370
|
-
const dir =
|
|
21371
|
-
const rawPath =
|
|
21372
|
-
const stderrPath =
|
|
21373
|
-
const commandPath =
|
|
21374
|
-
|
|
22621
|
+
const dir = import_node_path6.default.join(this.workspace.analysisDir(target), folder, job.id);
|
|
22622
|
+
const rawPath = import_node_path6.default.join(dir, "result.json");
|
|
22623
|
+
const stderrPath = import_node_path6.default.join(dir, "stderr.log");
|
|
22624
|
+
const commandPath = import_node_path6.default.join(dir, "command.json");
|
|
22625
|
+
import_node_fs7.default.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
21375
22626
|
this.workspace.writeJson(commandPath, {
|
|
21376
22627
|
command: this.config.reaPath,
|
|
21377
22628
|
args,
|
|
@@ -21390,7 +22641,7 @@ var CapabilityJobRunner = class {
|
|
|
21390
22641
|
});
|
|
21391
22642
|
this.workspace.writeText(rawPath, result.stdout);
|
|
21392
22643
|
this.workspace.writeText(stderrPath, result.stderr);
|
|
21393
|
-
this.workspace.writeJson(
|
|
22644
|
+
this.workspace.writeJson(import_node_path6.default.join(dir, "status.json"), {
|
|
21394
22645
|
code: result.code,
|
|
21395
22646
|
timedOut: result.timedOut,
|
|
21396
22647
|
finishedAt: now()
|
|
@@ -21404,7 +22655,7 @@ var CapabilityJobRunner = class {
|
|
|
21404
22655
|
if (result.code !== 0) {
|
|
21405
22656
|
const parsed2 = parseJsonDocument(result.stdout);
|
|
21406
22657
|
this.log(job, "warn", "\u5206\u6790\u5F15\u64CE\u672A\u6210\u529F\u5B8C\u6210\uFF0C\u539F\u59CB\u8F93\u51FA\u5DF2\u4FDD\u7559\u3002", {
|
|
21407
|
-
evidencePath:
|
|
22658
|
+
evidencePath: import_node_path6.default.relative(this.workspace.root, rawPath)
|
|
21408
22659
|
});
|
|
21409
22660
|
const message = reaErrorMessage(parsed2) ?? shortFailure(result.stderr, result.stdout);
|
|
21410
22661
|
fail("ENGINE_FAILED", message);
|
|
@@ -21418,7 +22669,7 @@ var CapabilityJobRunner = class {
|
|
|
21418
22669
|
}
|
|
21419
22670
|
return {
|
|
21420
22671
|
parsed,
|
|
21421
|
-
rawPath:
|
|
22672
|
+
rawPath: import_node_path6.default.relative(this.workspace.root, rawPath),
|
|
21422
22673
|
sha256: hashText(result.stdout),
|
|
21423
22674
|
summary: summarizeRea(parsed)
|
|
21424
22675
|
};
|
|
@@ -21466,10 +22717,10 @@ var CapabilityJobRunner = class {
|
|
|
21466
22717
|
}
|
|
21467
22718
|
};
|
|
21468
22719
|
function findAppPrefix(outputDir) {
|
|
21469
|
-
const payload =
|
|
21470
|
-
if (!
|
|
22720
|
+
const payload = import_node_path6.default.join(outputDir, "Payload");
|
|
22721
|
+
if (!import_node_fs7.default.existsSync(payload)) return void 0;
|
|
21471
22722
|
try {
|
|
21472
|
-
const app =
|
|
22723
|
+
const app = import_node_fs7.default.readdirSync(payload, { withFileTypes: true }).find((entry) => entry.isDirectory() && entry.name.endsWith(".app"));
|
|
21473
22724
|
return app ? `Payload/${app.name}/` : void 0;
|
|
21474
22725
|
} catch {
|
|
21475
22726
|
return void 0;
|
|
@@ -21477,14 +22728,17 @@ function findAppPrefix(outputDir) {
|
|
|
21477
22728
|
}
|
|
21478
22729
|
|
|
21479
22730
|
// src/license.ts
|
|
21480
|
-
var
|
|
22731
|
+
var import_node_fs8 = __toESM(require("fs"));
|
|
21481
22732
|
var import_node_crypto2 = __toESM(require("crypto"));
|
|
21482
22733
|
var FREE_FEATURES = /* @__PURE__ */ new Set([
|
|
21483
22734
|
"inspect:ipa",
|
|
21484
22735
|
"inspect:mac-app",
|
|
22736
|
+
"inspect:pe",
|
|
21485
22737
|
"extract:app",
|
|
21486
22738
|
"javascript:analyze",
|
|
21487
|
-
"generate:report"
|
|
22739
|
+
"generate:report",
|
|
22740
|
+
"native",
|
|
22741
|
+
"compare"
|
|
21488
22742
|
]);
|
|
21489
22743
|
function decode(value) {
|
|
21490
22744
|
return Buffer.from(value, "base64url");
|
|
@@ -21497,7 +22751,7 @@ function verifyLicense(token) {
|
|
|
21497
22751
|
if (!encodedPayload || !encodedSignature) {
|
|
21498
22752
|
throw new Error("Invalid license token.");
|
|
21499
22753
|
}
|
|
21500
|
-
const publicKeyValue = process.env.APPTRACE_LICENSE_PUBLIC_KEY || (process.env.APPTRACE_LICENSE_PUBLIC_KEY_FILE ?
|
|
22754
|
+
const publicKeyValue = process.env.APPTRACE_LICENSE_PUBLIC_KEY || (process.env.APPTRACE_LICENSE_PUBLIC_KEY_FILE ? import_node_fs8.default.readFileSync(process.env.APPTRACE_LICENSE_PUBLIC_KEY_FILE, "utf8") : void 0);
|
|
21501
22755
|
if (!publicKeyValue) {
|
|
21502
22756
|
throw new Error("APPTRACE_LICENSE_PUBLIC_KEY is not configured.");
|
|
21503
22757
|
}
|
|
@@ -21554,8 +22808,8 @@ function compareVersionData(input2) {
|
|
|
21554
22808
|
}
|
|
21555
22809
|
|
|
21556
22810
|
// src/store.ts
|
|
21557
|
-
var
|
|
21558
|
-
var
|
|
22811
|
+
var import_node_fs9 = __toESM(require("fs"));
|
|
22812
|
+
var import_node_path7 = __toESM(require("path"));
|
|
21559
22813
|
var import_node_module = require("module");
|
|
21560
22814
|
var import_meta = {};
|
|
21561
22815
|
function openSqlite(databasePath) {
|
|
@@ -21770,11 +23024,11 @@ function evidenceFromRow(row) {
|
|
|
21770
23024
|
};
|
|
21771
23025
|
}
|
|
21772
23026
|
function findingFromRow(row) {
|
|
21773
|
-
const
|
|
21774
|
-
const startLine =
|
|
21775
|
-
const startColumn =
|
|
21776
|
-
const endLine =
|
|
21777
|
-
const endColumn =
|
|
23027
|
+
const numberValue2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
23028
|
+
const startLine = numberValue2(row.start_line);
|
|
23029
|
+
const startColumn = numberValue2(row.start_column);
|
|
23030
|
+
const endLine = numberValue2(row.end_line);
|
|
23031
|
+
const endColumn = numberValue2(row.end_column);
|
|
21778
23032
|
return {
|
|
21779
23033
|
id: String(row.id),
|
|
21780
23034
|
targetId: String(row.target_id),
|
|
@@ -21808,7 +23062,7 @@ var SqliteStore = class {
|
|
|
21808
23062
|
kind = "sqlite";
|
|
21809
23063
|
db;
|
|
21810
23064
|
constructor(databasePath) {
|
|
21811
|
-
|
|
23065
|
+
import_node_fs9.default.mkdirSync(import_node_path7.default.dirname(databasePath), { recursive: true, mode: 448 });
|
|
21812
23066
|
const db = openSqlite(databasePath);
|
|
21813
23067
|
if (!db) throw new Error("node:sqlite is not available");
|
|
21814
23068
|
this.db = db;
|
|
@@ -22141,11 +23395,11 @@ var JsonStore = class {
|
|
|
22141
23395
|
data;
|
|
22142
23396
|
constructor(databasePath) {
|
|
22143
23397
|
this.filePath = databasePath.replace(/\.sqlite$/i, "") + ".json";
|
|
22144
|
-
|
|
23398
|
+
import_node_fs9.default.mkdirSync(import_node_path7.default.dirname(this.filePath), { recursive: true, mode: 448 });
|
|
22145
23399
|
this.data = this.load();
|
|
22146
23400
|
}
|
|
22147
23401
|
load() {
|
|
22148
|
-
if (!
|
|
23402
|
+
if (!import_node_fs9.default.existsSync(this.filePath)) {
|
|
22149
23403
|
return {
|
|
22150
23404
|
targets: [],
|
|
22151
23405
|
artifacts: [],
|
|
@@ -22157,7 +23411,7 @@ var JsonStore = class {
|
|
|
22157
23411
|
};
|
|
22158
23412
|
}
|
|
22159
23413
|
try {
|
|
22160
|
-
const parsed = JSON.parse(
|
|
23414
|
+
const parsed = JSON.parse(import_node_fs9.default.readFileSync(this.filePath, "utf8"));
|
|
22161
23415
|
return {
|
|
22162
23416
|
targets: parsed.targets ?? [],
|
|
22163
23417
|
artifacts: parsed.artifacts ?? [],
|
|
@@ -22181,8 +23435,8 @@ var JsonStore = class {
|
|
|
22181
23435
|
}
|
|
22182
23436
|
save() {
|
|
22183
23437
|
const temp = `${this.filePath}.${process.pid}.tmp`;
|
|
22184
|
-
|
|
22185
|
-
|
|
23438
|
+
import_node_fs9.default.writeFileSync(temp, JSON.stringify(this.data, null, 2), { mode: 384 });
|
|
23439
|
+
import_node_fs9.default.renameSync(temp, this.filePath);
|
|
22186
23440
|
}
|
|
22187
23441
|
listTargets() {
|
|
22188
23442
|
return [...this.data.targets].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
@@ -22323,44 +23577,44 @@ function createStore(databasePath) {
|
|
|
22323
23577
|
}
|
|
22324
23578
|
|
|
22325
23579
|
// src/workspace.ts
|
|
22326
|
-
var
|
|
22327
|
-
var
|
|
23580
|
+
var import_node_fs10 = __toESM(require("fs"));
|
|
23581
|
+
var import_node_path8 = __toESM(require("path"));
|
|
22328
23582
|
var WorkspaceService = class {
|
|
22329
23583
|
root;
|
|
22330
23584
|
constructor(root) {
|
|
22331
|
-
this.root =
|
|
22332
|
-
|
|
22333
|
-
|
|
22334
|
-
|
|
23585
|
+
this.root = import_node_path8.default.resolve(root);
|
|
23586
|
+
import_node_fs10.default.mkdirSync(this.root, { recursive: true, mode: 448 });
|
|
23587
|
+
import_node_fs10.default.mkdirSync(import_node_path8.default.join(this.root, "targets"), { recursive: true, mode: 448 });
|
|
23588
|
+
import_node_fs10.default.mkdirSync(import_node_path8.default.join(this.root, "tmp"), { recursive: true, mode: 448 });
|
|
22335
23589
|
}
|
|
22336
23590
|
targetDir(target) {
|
|
22337
|
-
return
|
|
23591
|
+
return import_node_path8.default.join(
|
|
22338
23592
|
this.root,
|
|
22339
23593
|
"targets",
|
|
22340
23594
|
`${target.targetRef.platform}-${target.targetRef.sourceId}-${target.slug}-${target.id.slice(0, 8)}`
|
|
22341
23595
|
);
|
|
22342
23596
|
}
|
|
22343
23597
|
artifactDir(target) {
|
|
22344
|
-
return
|
|
23598
|
+
return import_node_path8.default.join(this.targetDir(target), "artifacts");
|
|
22345
23599
|
}
|
|
22346
23600
|
analysisDir(target) {
|
|
22347
|
-
return
|
|
23601
|
+
return import_node_path8.default.join(this.targetDir(target), "analysis");
|
|
22348
23602
|
}
|
|
22349
23603
|
ensureTarget(target) {
|
|
22350
23604
|
const dir = this.targetDir(target);
|
|
22351
23605
|
for (const subdir of ["", "artifacts", "analysis", "reports"]) {
|
|
22352
|
-
|
|
23606
|
+
import_node_fs10.default.mkdirSync(import_node_path8.default.join(dir, subdir), { recursive: true, mode: 448 });
|
|
22353
23607
|
}
|
|
22354
23608
|
}
|
|
22355
23609
|
saveResolvedTarget(target, rawSource) {
|
|
22356
23610
|
this.ensureTarget(target);
|
|
22357
23611
|
const dir = this.targetDir(target);
|
|
22358
|
-
this.writeJson(
|
|
22359
|
-
this.writeJson(
|
|
23612
|
+
this.writeJson(import_node_path8.default.join(dir, "meta.json"), target);
|
|
23613
|
+
this.writeJson(import_node_path8.default.join(dir, "source.json"), rawSource);
|
|
22360
23614
|
}
|
|
22361
23615
|
saveArtifactMetadata(target, artifact, extra = {}) {
|
|
22362
23616
|
const dir = this.targetDir(target);
|
|
22363
|
-
this.writeJson(
|
|
23617
|
+
this.writeJson(import_node_path8.default.join(dir, "artifact.json"), {
|
|
22364
23618
|
schemaVersion: 1,
|
|
22365
23619
|
target: {
|
|
22366
23620
|
id: target.id,
|
|
@@ -22384,7 +23638,7 @@ var WorkspaceService = class {
|
|
|
22384
23638
|
},
|
|
22385
23639
|
...extra
|
|
22386
23640
|
});
|
|
22387
|
-
this.writeJson(
|
|
23641
|
+
this.writeJson(import_node_path8.default.join(dir, "current.json"), {
|
|
22388
23642
|
artifactId: artifact.id,
|
|
22389
23643
|
path: artifact.path,
|
|
22390
23644
|
sha256: artifact.sha256,
|
|
@@ -22392,11 +23646,11 @@ var WorkspaceService = class {
|
|
|
22392
23646
|
});
|
|
22393
23647
|
}
|
|
22394
23648
|
artifactRelativePath(target, filePath) {
|
|
22395
|
-
return
|
|
23649
|
+
return import_node_path8.default.relative(this.root, filePath);
|
|
22396
23650
|
}
|
|
22397
23651
|
resolveStoredPath(relativePath) {
|
|
22398
|
-
const resolved =
|
|
22399
|
-
if (resolved !== this.root && !resolved.startsWith(this.root +
|
|
23652
|
+
const resolved = import_node_path8.default.resolve(this.root, relativePath);
|
|
23653
|
+
if (resolved !== this.root && !resolved.startsWith(this.root + import_node_path8.default.sep)) {
|
|
22400
23654
|
throw new Error(`Path escapes workspace: ${relativePath}`);
|
|
22401
23655
|
}
|
|
22402
23656
|
return resolved;
|
|
@@ -22406,10 +23660,10 @@ var WorkspaceService = class {
|
|
|
22406
23660
|
`);
|
|
22407
23661
|
}
|
|
22408
23662
|
writeText(filePath, value) {
|
|
22409
|
-
|
|
23663
|
+
import_node_fs10.default.mkdirSync(import_node_path8.default.dirname(filePath), { recursive: true, mode: 448 });
|
|
22410
23664
|
const temp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
22411
|
-
|
|
22412
|
-
|
|
23665
|
+
import_node_fs10.default.writeFileSync(temp, value, { mode: 384 });
|
|
23666
|
+
import_node_fs10.default.renameSync(temp, filePath);
|
|
22413
23667
|
}
|
|
22414
23668
|
};
|
|
22415
23669
|
|
|
@@ -22448,6 +23702,8 @@ async function handle(line) {
|
|
|
22448
23702
|
ok: true,
|
|
22449
23703
|
result: { protocolVersion: PROTOCOL_VERSION, capabilities: [
|
|
22450
23704
|
"inspect:ipa",
|
|
23705
|
+
"inspect:mac-app",
|
|
23706
|
+
"inspect:pe",
|
|
22451
23707
|
"extract:app",
|
|
22452
23708
|
"javascript:analyze",
|
|
22453
23709
|
"native:inspect",
|
|
@@ -22475,12 +23731,12 @@ async function handle(line) {
|
|
|
22475
23731
|
});
|
|
22476
23732
|
return;
|
|
22477
23733
|
}
|
|
22478
|
-
const workspaceRoot =
|
|
22479
|
-
if (!
|
|
23734
|
+
const workspaceRoot = import_node_path9.default.resolve(request.workspacePath);
|
|
23735
|
+
if (!import_node_fs11.default.existsSync(workspaceRoot)) {
|
|
22480
23736
|
send({ id: request.id, ok: false, error: { code: "WORKSPACE_NOT_FOUND", message: workspaceRoot } });
|
|
22481
23737
|
return;
|
|
22482
23738
|
}
|
|
22483
|
-
const store = createStore(
|
|
23739
|
+
const store = createStore(import_node_path9.default.join(workspaceRoot, "workbench.sqlite"));
|
|
22484
23740
|
try {
|
|
22485
23741
|
if (request.capability === "compare:versions") {
|
|
22486
23742
|
send({
|