@zhuixin/engine 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/engine-main.js +1187 -150
- 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"],
|
|
19476
|
-
packageFormats: ["ipa"],
|
|
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 {
|
|
@@ -20074,6 +20074,872 @@ async function inspectMacApp(bundlePath) {
|
|
|
20074
20074
|
};
|
|
20075
20075
|
}
|
|
20076
20076
|
|
|
20077
|
+
// src/inspectors/windowsApp.ts
|
|
20078
|
+
var import_node_child_process2 = require("child_process");
|
|
20079
|
+
var import_node_fs6 = __toESM(require("fs"));
|
|
20080
|
+
var import_node_path4 = __toESM(require("path"));
|
|
20081
|
+
var import_node_util2 = require("util");
|
|
20082
|
+
|
|
20083
|
+
// src/inspectors/pe.ts
|
|
20084
|
+
var import_node_fs5 = __toESM(require("fs"));
|
|
20085
|
+
var MACHINES = {
|
|
20086
|
+
332: "x86",
|
|
20087
|
+
34404: "x64",
|
|
20088
|
+
448: "arm",
|
|
20089
|
+
452: "arm (Thumb-2)",
|
|
20090
|
+
43620: "arm64",
|
|
20091
|
+
42561: "arm64ec",
|
|
20092
|
+
42574: "arm64x",
|
|
20093
|
+
512: "ia64"
|
|
20094
|
+
};
|
|
20095
|
+
var SUBSYSTEMS = {
|
|
20096
|
+
1: "native",
|
|
20097
|
+
2: "windows-gui",
|
|
20098
|
+
3: "windows-console",
|
|
20099
|
+
5: "os2-console",
|
|
20100
|
+
7: "posix-console",
|
|
20101
|
+
9: "windows-ce-gui",
|
|
20102
|
+
10: "efi-application",
|
|
20103
|
+
11: "efi-boot-service-driver",
|
|
20104
|
+
12: "efi-runtime-driver",
|
|
20105
|
+
14: "xbox",
|
|
20106
|
+
16: "windows-boot-application"
|
|
20107
|
+
};
|
|
20108
|
+
var CERTIFICATE_TYPES = {
|
|
20109
|
+
1: "x509",
|
|
20110
|
+
2: "pkcs-signed-data",
|
|
20111
|
+
3: "reserved",
|
|
20112
|
+
4: "ts-stack-signed"
|
|
20113
|
+
};
|
|
20114
|
+
var MAX_SECTIONS = 96;
|
|
20115
|
+
var MAX_IMPORT_LIBRARIES = 256;
|
|
20116
|
+
var MAX_FUNCTIONS_PER_LIBRARY = 12;
|
|
20117
|
+
var MAX_THUNKS_PER_LIBRARY = 2e4;
|
|
20118
|
+
var ENTROPY_SAMPLE_BYTES = 4 * 1024 * 1024;
|
|
20119
|
+
var RESOURCE_MAX_DEPTH = 4;
|
|
20120
|
+
function fileReader(fd, size) {
|
|
20121
|
+
return {
|
|
20122
|
+
size,
|
|
20123
|
+
read(offset, length) {
|
|
20124
|
+
if (!Number.isSafeInteger(offset) || offset < 0 || length < 0) return void 0;
|
|
20125
|
+
if (offset + length > size) return void 0;
|
|
20126
|
+
const buffer = Buffer.alloc(length);
|
|
20127
|
+
let done = 0;
|
|
20128
|
+
while (done < length) {
|
|
20129
|
+
const count = import_node_fs5.default.readSync(fd, buffer, done, length - done, offset + done);
|
|
20130
|
+
if (count <= 0) return void 0;
|
|
20131
|
+
done += count;
|
|
20132
|
+
}
|
|
20133
|
+
return buffer;
|
|
20134
|
+
}
|
|
20135
|
+
};
|
|
20136
|
+
}
|
|
20137
|
+
function hex(value) {
|
|
20138
|
+
return `0x${value.toString(16)}`;
|
|
20139
|
+
}
|
|
20140
|
+
function cString(buffer, start = 0, limit = buffer.length) {
|
|
20141
|
+
let end = start;
|
|
20142
|
+
const max = Math.min(limit, buffer.length);
|
|
20143
|
+
while (end < max && buffer[end] !== 0) end += 1;
|
|
20144
|
+
return buffer.toString("latin1", start, end);
|
|
20145
|
+
}
|
|
20146
|
+
function entropyOf(buffer) {
|
|
20147
|
+
if (buffer.length === 0) return 0;
|
|
20148
|
+
const counts = new Array(256).fill(0);
|
|
20149
|
+
for (const byte of buffer) counts[byte] += 1;
|
|
20150
|
+
let entropy = 0;
|
|
20151
|
+
for (const count of counts) {
|
|
20152
|
+
if (count === 0) continue;
|
|
20153
|
+
const p = count / buffer.length;
|
|
20154
|
+
entropy -= p * Math.log2(p);
|
|
20155
|
+
}
|
|
20156
|
+
return Math.round(entropy * 100) / 100;
|
|
20157
|
+
}
|
|
20158
|
+
function isPeFile(filePath) {
|
|
20159
|
+
let fd;
|
|
20160
|
+
try {
|
|
20161
|
+
fd = import_node_fs5.default.openSync(filePath, "r");
|
|
20162
|
+
const head = Buffer.alloc(2);
|
|
20163
|
+
return import_node_fs5.default.readSync(fd, head, 0, 2, 0) === 2 && head[0] === 77 && head[1] === 90;
|
|
20164
|
+
} catch {
|
|
20165
|
+
return false;
|
|
20166
|
+
} finally {
|
|
20167
|
+
if (fd !== void 0) import_node_fs5.default.closeSync(fd);
|
|
20168
|
+
}
|
|
20169
|
+
}
|
|
20170
|
+
function inspectPeFile(filePath) {
|
|
20171
|
+
const fd = import_node_fs5.default.openSync(filePath, "r");
|
|
20172
|
+
try {
|
|
20173
|
+
const size = import_node_fs5.default.fstatSync(fd).size;
|
|
20174
|
+
return inspectPe(fileReader(fd, size));
|
|
20175
|
+
} finally {
|
|
20176
|
+
import_node_fs5.default.closeSync(fd);
|
|
20177
|
+
}
|
|
20178
|
+
}
|
|
20179
|
+
function inspectPe(reader) {
|
|
20180
|
+
const warnings = [];
|
|
20181
|
+
const dos = reader.read(0, 64);
|
|
20182
|
+
if (!dos || dos.readUInt16LE(0) !== 23117) {
|
|
20183
|
+
throw new Error("Not a PE file: the MZ signature is missing.");
|
|
20184
|
+
}
|
|
20185
|
+
const peOffset = dos.readUInt32LE(60);
|
|
20186
|
+
const signature = reader.read(peOffset, 4);
|
|
20187
|
+
if (!signature || signature.toString("latin1") !== "PE\0\0") {
|
|
20188
|
+
throw new Error("Not a PE file: the PE signature is missing.");
|
|
20189
|
+
}
|
|
20190
|
+
const coff = reader.read(peOffset + 4, 20);
|
|
20191
|
+
if (!coff) throw new Error("The COFF header is truncated.");
|
|
20192
|
+
const machine = coff.readUInt16LE(0);
|
|
20193
|
+
const sectionCount = coff.readUInt16LE(2);
|
|
20194
|
+
const timeDateStamp = coff.readUInt32LE(4);
|
|
20195
|
+
const optionalSize = coff.readUInt16LE(16);
|
|
20196
|
+
const characteristics = coff.readUInt16LE(18);
|
|
20197
|
+
const optionalOffset = peOffset + 24;
|
|
20198
|
+
const optional = reader.read(optionalOffset, Math.min(optionalSize, 240));
|
|
20199
|
+
if (!optional || optional.length < 96) {
|
|
20200
|
+
throw new Error("The optional header is truncated.");
|
|
20201
|
+
}
|
|
20202
|
+
const magic = optional.readUInt16LE(0);
|
|
20203
|
+
if (magic !== 267 && magic !== 523) {
|
|
20204
|
+
throw new Error(`Unsupported optional header magic ${hex(magic)}.`);
|
|
20205
|
+
}
|
|
20206
|
+
const plus = magic === 523;
|
|
20207
|
+
const directoriesOffset = plus ? 112 : 96;
|
|
20208
|
+
const entryPointRva = optional.readUInt32LE(16);
|
|
20209
|
+
const imageBase = plus ? optional.readBigUInt64LE(24) : BigInt(optional.readUInt32LE(28));
|
|
20210
|
+
const linker = `${optional[2]}.${optional[3]}`;
|
|
20211
|
+
const minimumOs = `${optional.readUInt16LE(40)}.${optional.readUInt16LE(42)}`;
|
|
20212
|
+
const subsystemId = optional.readUInt16LE(68);
|
|
20213
|
+
const dllCharacteristics = optional.readUInt16LE(70);
|
|
20214
|
+
const declaredDirectories = optional.length >= directoriesOffset ? optional.readUInt32LE(directoriesOffset - 4) : 0;
|
|
20215
|
+
const directory = (index) => {
|
|
20216
|
+
if (index >= declaredDirectories || index >= 16) return void 0;
|
|
20217
|
+
const at = directoriesOffset + index * 8;
|
|
20218
|
+
if (at + 8 > optional.length) return void 0;
|
|
20219
|
+
const rva = optional.readUInt32LE(at);
|
|
20220
|
+
const sizeValue = optional.readUInt32LE(at + 4);
|
|
20221
|
+
return rva === 0 && sizeValue === 0 ? void 0 : { rva, size: sizeValue };
|
|
20222
|
+
};
|
|
20223
|
+
const sections = [];
|
|
20224
|
+
const sectionTableOffset = optionalOffset + optionalSize;
|
|
20225
|
+
const boundedCount = Math.min(sectionCount, MAX_SECTIONS);
|
|
20226
|
+
if (sectionCount > MAX_SECTIONS) {
|
|
20227
|
+
warnings.push(`The file declares ${sectionCount} sections; only the first ${MAX_SECTIONS} were read.`);
|
|
20228
|
+
}
|
|
20229
|
+
for (let index = 0; index < boundedCount; index += 1) {
|
|
20230
|
+
const raw = reader.read(sectionTableOffset + index * 40, 40);
|
|
20231
|
+
if (!raw) {
|
|
20232
|
+
warnings.push("The section table is truncated.");
|
|
20233
|
+
break;
|
|
20234
|
+
}
|
|
20235
|
+
const sectionCharacteristics = raw.readUInt32LE(36);
|
|
20236
|
+
const rawSize = raw.readUInt32LE(16);
|
|
20237
|
+
const rawOffset = raw.readUInt32LE(20);
|
|
20238
|
+
const section = {
|
|
20239
|
+
name: cString(raw, 0, 8),
|
|
20240
|
+
virtualSize: raw.readUInt32LE(8),
|
|
20241
|
+
rawSize,
|
|
20242
|
+
rawOffset,
|
|
20243
|
+
characteristics: sectionCharacteristics,
|
|
20244
|
+
executable: (sectionCharacteristics & 536870912) !== 0,
|
|
20245
|
+
writable: (sectionCharacteristics & 2147483648) !== 0
|
|
20246
|
+
};
|
|
20247
|
+
if (rawSize > 0 && rawOffset > 0) {
|
|
20248
|
+
const sample = reader.read(rawOffset, Math.min(rawSize, ENTROPY_SAMPLE_BYTES));
|
|
20249
|
+
if (sample) section.entropy = entropyOf(sample);
|
|
20250
|
+
}
|
|
20251
|
+
sections.push(section);
|
|
20252
|
+
}
|
|
20253
|
+
const sectionRva = raws(sectionTableOffset, reader, boundedCount);
|
|
20254
|
+
const rvaToOffset = (rva) => {
|
|
20255
|
+
for (const entry of sectionRva) {
|
|
20256
|
+
const span = Math.max(entry.virtualSize, entry.rawSize);
|
|
20257
|
+
if (rva >= entry.rva && rva < entry.rva + span) {
|
|
20258
|
+
const offset = entry.rawOffset + (rva - entry.rva);
|
|
20259
|
+
return offset < reader.size ? offset : void 0;
|
|
20260
|
+
}
|
|
20261
|
+
}
|
|
20262
|
+
return rva < reader.size && sectionRva.every((entry) => rva < entry.rva) ? rva : void 0;
|
|
20263
|
+
};
|
|
20264
|
+
const readRva = (rva, length) => {
|
|
20265
|
+
const offset = rvaToOffset(rva);
|
|
20266
|
+
return offset === void 0 ? void 0 : reader.read(offset, length);
|
|
20267
|
+
};
|
|
20268
|
+
const stringAtRva = (rva, max = 256) => {
|
|
20269
|
+
const offset = rvaToOffset(rva);
|
|
20270
|
+
if (offset === void 0) return void 0;
|
|
20271
|
+
const chunk = reader.read(offset, Math.min(max, reader.size - offset));
|
|
20272
|
+
return chunk ? cString(chunk) : void 0;
|
|
20273
|
+
};
|
|
20274
|
+
const imports = { libraryCount: 0, libraries: [], truncated: false };
|
|
20275
|
+
const importDirectory = directory(1);
|
|
20276
|
+
if (importDirectory) {
|
|
20277
|
+
const thunkSize = plus ? 8 : 4;
|
|
20278
|
+
const collectThunks = (lookupRva, library) => {
|
|
20279
|
+
if (!lookupRva) return;
|
|
20280
|
+
for (let i = 0; i < MAX_THUNKS_PER_LIBRARY; i += 1) {
|
|
20281
|
+
const thunk = readRva(lookupRva + i * thunkSize, thunkSize);
|
|
20282
|
+
if (!thunk) break;
|
|
20283
|
+
const value = plus ? thunk.readBigUInt64LE(0) : BigInt(thunk.readUInt32LE(0));
|
|
20284
|
+
if (value === 0n) break;
|
|
20285
|
+
library.functionCount += 1;
|
|
20286
|
+
if (library.functions.length >= MAX_FUNCTIONS_PER_LIBRARY) continue;
|
|
20287
|
+
const ordinalFlag = plus ? 1n << 63n : 1n << 31n;
|
|
20288
|
+
if (value & ordinalFlag) {
|
|
20289
|
+
library.functions.push(`#${Number(value & 0xffffn)}`);
|
|
20290
|
+
} else {
|
|
20291
|
+
const name = stringAtRva(Number(value & 0x7fffffffn) + 2, 256);
|
|
20292
|
+
if (name) library.functions.push(name);
|
|
20293
|
+
}
|
|
20294
|
+
}
|
|
20295
|
+
};
|
|
20296
|
+
for (let index = 0; index < 4096; index += 1) {
|
|
20297
|
+
const descriptor = readRva(importDirectory.rva + index * 20, 20);
|
|
20298
|
+
if (!descriptor) {
|
|
20299
|
+
if (index === 0) warnings.push("The import table could not be read.");
|
|
20300
|
+
break;
|
|
20301
|
+
}
|
|
20302
|
+
const nameRva = descriptor.readUInt32LE(12);
|
|
20303
|
+
const lookupRva = descriptor.readUInt32LE(0) || descriptor.readUInt32LE(16);
|
|
20304
|
+
if (nameRva === 0 && lookupRva === 0) break;
|
|
20305
|
+
imports.libraryCount += 1;
|
|
20306
|
+
if (imports.libraries.length >= MAX_IMPORT_LIBRARIES) {
|
|
20307
|
+
imports.truncated = true;
|
|
20308
|
+
continue;
|
|
20309
|
+
}
|
|
20310
|
+
const library = {
|
|
20311
|
+
name: stringAtRva(nameRva, 260) ?? "?",
|
|
20312
|
+
functionCount: 0,
|
|
20313
|
+
functions: [],
|
|
20314
|
+
delayLoaded: false
|
|
20315
|
+
};
|
|
20316
|
+
collectThunks(lookupRva, library);
|
|
20317
|
+
imports.libraries.push(library);
|
|
20318
|
+
}
|
|
20319
|
+
}
|
|
20320
|
+
const delayDirectory = directory(13);
|
|
20321
|
+
if (delayDirectory) {
|
|
20322
|
+
for (let index = 0; index < 64; index += 1) {
|
|
20323
|
+
const descriptor = readRva(delayDirectory.rva + index * 32, 32);
|
|
20324
|
+
if (!descriptor) break;
|
|
20325
|
+
const attributes = descriptor.readUInt32LE(0);
|
|
20326
|
+
const nameField = descriptor.readUInt32LE(4);
|
|
20327
|
+
if (nameField === 0) break;
|
|
20328
|
+
const nameRva = attributes & 1 ? nameField : nameField - Number(imageBase & 0xffffffffn);
|
|
20329
|
+
const name = stringAtRva(nameRva, 260);
|
|
20330
|
+
if (!name) continue;
|
|
20331
|
+
imports.libraryCount += 1;
|
|
20332
|
+
if (imports.libraries.length < MAX_IMPORT_LIBRARIES) {
|
|
20333
|
+
imports.libraries.push({ name, functionCount: 0, functions: [], delayLoaded: true });
|
|
20334
|
+
} else {
|
|
20335
|
+
imports.truncated = true;
|
|
20336
|
+
}
|
|
20337
|
+
}
|
|
20338
|
+
}
|
|
20339
|
+
const exportsInfo = { count: 0, namedCount: 0 };
|
|
20340
|
+
const exportDirectory = directory(0);
|
|
20341
|
+
if (exportDirectory) {
|
|
20342
|
+
const table = readRva(exportDirectory.rva, 40);
|
|
20343
|
+
if (table) {
|
|
20344
|
+
exportsInfo.count = table.readUInt32LE(20);
|
|
20345
|
+
exportsInfo.namedCount = table.readUInt32LE(24);
|
|
20346
|
+
exportsInfo.dllName = stringAtRva(table.readUInt32LE(12), 260);
|
|
20347
|
+
}
|
|
20348
|
+
}
|
|
20349
|
+
let dotnet;
|
|
20350
|
+
const clrDirectory = directory(14);
|
|
20351
|
+
if (clrDirectory) {
|
|
20352
|
+
const header = readRva(clrDirectory.rva, 72);
|
|
20353
|
+
if (header) {
|
|
20354
|
+
const flags = header.readUInt32LE(16);
|
|
20355
|
+
dotnet = {
|
|
20356
|
+
runtimeVersion: `${header.readUInt16LE(4)}.${header.readUInt16LE(6)}`,
|
|
20357
|
+
ilOnly: (flags & 1) !== 0,
|
|
20358
|
+
requires32Bit: (flags & 2) !== 0,
|
|
20359
|
+
strongNameSigned: (flags & 8) !== 0,
|
|
20360
|
+
nativeEntryPoint: (flags & 16) !== 0
|
|
20361
|
+
};
|
|
20362
|
+
const metadataRva = header.readUInt32LE(8);
|
|
20363
|
+
const root = metadataRva ? readRva(metadataRva, 32) : void 0;
|
|
20364
|
+
if (root && root.readUInt32LE(0) === 1112167234) {
|
|
20365
|
+
const versionLength = root.readUInt32LE(12);
|
|
20366
|
+
if (versionLength > 0 && versionLength <= 255) {
|
|
20367
|
+
const version2 = readRva(metadataRva + 16, versionLength);
|
|
20368
|
+
if (version2) dotnet.metadataVersion = cString(version2);
|
|
20369
|
+
}
|
|
20370
|
+
}
|
|
20371
|
+
}
|
|
20372
|
+
}
|
|
20373
|
+
const version = readVersionInfo(directory(2), readRva, rvaToOffset, reader, warnings);
|
|
20374
|
+
let debug;
|
|
20375
|
+
const debugDirectory = directory(6);
|
|
20376
|
+
if (debugDirectory) {
|
|
20377
|
+
const entries = Math.min(Math.floor(debugDirectory.size / 28), 16);
|
|
20378
|
+
for (let index = 0; index < entries && !debug; index += 1) {
|
|
20379
|
+
const entry = readRva(debugDirectory.rva + index * 28, 28);
|
|
20380
|
+
if (!entry || entry.readUInt32LE(12) !== 2) continue;
|
|
20381
|
+
const dataSize = entry.readUInt32LE(16);
|
|
20382
|
+
const pointer = entry.readUInt32LE(24);
|
|
20383
|
+
const data = pointer && dataSize >= 24 && dataSize < 4096 ? reader.read(pointer, dataSize) : void 0;
|
|
20384
|
+
if (data && data.toString("latin1", 0, 4) === "RSDS") {
|
|
20385
|
+
debug = {
|
|
20386
|
+
guid: guidString(data.subarray(4, 20)),
|
|
20387
|
+
age: data.readUInt32LE(20),
|
|
20388
|
+
pdbPath: cString(data, 24)
|
|
20389
|
+
};
|
|
20390
|
+
}
|
|
20391
|
+
}
|
|
20392
|
+
}
|
|
20393
|
+
const certificate = { present: false, sizeBytes: 0 };
|
|
20394
|
+
const certificateEntry = directory(4);
|
|
20395
|
+
if (certificateEntry) {
|
|
20396
|
+
certificate.sizeBytes = certificateEntry.size;
|
|
20397
|
+
const header = reader.read(certificateEntry.rva, 8);
|
|
20398
|
+
if (header) {
|
|
20399
|
+
certificate.present = true;
|
|
20400
|
+
certificate.type = CERTIFICATE_TYPES[header.readUInt16LE(6)] ?? `type-${header.readUInt16LE(6)}`;
|
|
20401
|
+
} else {
|
|
20402
|
+
warnings.push("The certificate table points outside the file.");
|
|
20403
|
+
}
|
|
20404
|
+
}
|
|
20405
|
+
let endOfImage = 0;
|
|
20406
|
+
for (const section of sections) {
|
|
20407
|
+
endOfImage = Math.max(endOfImage, section.rawOffset + section.rawSize);
|
|
20408
|
+
}
|
|
20409
|
+
const certificateStart = certificate.present ? certificateEntry.rva : reader.size;
|
|
20410
|
+
const overlayEnd = Math.min(reader.size, certificateStart);
|
|
20411
|
+
const overlaySizeBytes = Math.max(0, overlayEnd - endOfImage);
|
|
20412
|
+
const mitigations = [];
|
|
20413
|
+
if (dllCharacteristics & 64) mitigations.push("ASLR");
|
|
20414
|
+
if (dllCharacteristics & 32) mitigations.push("high-entropy-ASLR");
|
|
20415
|
+
if (dllCharacteristics & 256) mitigations.push("DEP");
|
|
20416
|
+
if (dllCharacteristics & 16384) mitigations.push("CFG");
|
|
20417
|
+
if (dllCharacteristics & 1024) mitigations.push("no-SEH");
|
|
20418
|
+
if (dllCharacteristics & 4096) mitigations.push("app-container");
|
|
20419
|
+
const machineName = MACHINES[machine] ?? `machine-${hex(machine)}`;
|
|
20420
|
+
const reproducible = debugHasReproFlag(debugDirectory, readRva) || timeDateStamp === 0;
|
|
20421
|
+
const timestamp = timeDateStamp !== 0 && !reproducible ? new Date(timeDateStamp * 1e3).toISOString() : void 0;
|
|
20422
|
+
return {
|
|
20423
|
+
kind: "pe",
|
|
20424
|
+
sizeBytes: reader.size,
|
|
20425
|
+
format: plus ? "PE32+" : "PE32",
|
|
20426
|
+
architecture: machineName,
|
|
20427
|
+
isDll: (characteristics & 8192) !== 0,
|
|
20428
|
+
subsystem: SUBSYSTEMS[subsystemId] ?? `subsystem-${subsystemId}`,
|
|
20429
|
+
timestamp,
|
|
20430
|
+
timestampIsReproducible: reproducible,
|
|
20431
|
+
entryPointRva,
|
|
20432
|
+
imageBase: hex(imageBase),
|
|
20433
|
+
linkerVersion: linker,
|
|
20434
|
+
minimumOsVersion: minimumOs,
|
|
20435
|
+
mitigations,
|
|
20436
|
+
sections,
|
|
20437
|
+
imports,
|
|
20438
|
+
exports: exportsInfo,
|
|
20439
|
+
dotnet,
|
|
20440
|
+
version,
|
|
20441
|
+
debug,
|
|
20442
|
+
certificate,
|
|
20443
|
+
overlaySizeBytes,
|
|
20444
|
+
warnings
|
|
20445
|
+
};
|
|
20446
|
+
}
|
|
20447
|
+
function raws(tableOffset, reader, count) {
|
|
20448
|
+
const result = [];
|
|
20449
|
+
for (let index = 0; index < count; index += 1) {
|
|
20450
|
+
const raw = reader.read(tableOffset + index * 40, 40);
|
|
20451
|
+
if (!raw) break;
|
|
20452
|
+
result.push({
|
|
20453
|
+
virtualSize: raw.readUInt32LE(8),
|
|
20454
|
+
rva: raw.readUInt32LE(12),
|
|
20455
|
+
rawSize: raw.readUInt32LE(16),
|
|
20456
|
+
rawOffset: raw.readUInt32LE(20)
|
|
20457
|
+
});
|
|
20458
|
+
}
|
|
20459
|
+
return result;
|
|
20460
|
+
}
|
|
20461
|
+
function guidString(bytes) {
|
|
20462
|
+
const part = (start, end) => [...bytes.subarray(start, end)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
20463
|
+
const reverse = (start, end) => [...bytes.subarray(start, end)].reverse().map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
20464
|
+
return `${reverse(0, 4)}-${reverse(4, 6)}-${reverse(6, 8)}-${part(8, 10)}-${part(10, 16)}`;
|
|
20465
|
+
}
|
|
20466
|
+
function debugHasReproFlag(directory, readRva) {
|
|
20467
|
+
if (!directory) return false;
|
|
20468
|
+
const entries = Math.min(Math.floor(directory.size / 28), 16);
|
|
20469
|
+
for (let index = 0; index < entries; index += 1) {
|
|
20470
|
+
const entry = readRva(directory.rva + index * 28, 28);
|
|
20471
|
+
if (entry && entry.readUInt32LE(12) === 16) return true;
|
|
20472
|
+
}
|
|
20473
|
+
return false;
|
|
20474
|
+
}
|
|
20475
|
+
function readVersionInfo(resources, readRva, rvaToOffset, reader, warnings) {
|
|
20476
|
+
if (!resources) return void 0;
|
|
20477
|
+
const base = resources.rva;
|
|
20478
|
+
const readDirectory = (offset2) => {
|
|
20479
|
+
const header = readRva(base + offset2, 16);
|
|
20480
|
+
if (!header) return void 0;
|
|
20481
|
+
const named = header.readUInt16LE(12);
|
|
20482
|
+
const ids = header.readUInt16LE(14);
|
|
20483
|
+
const count = Math.min(named + ids, 512);
|
|
20484
|
+
const table = count ? readRva(base + offset2 + 16, count * 8) : Buffer.alloc(0);
|
|
20485
|
+
if (!table) return void 0;
|
|
20486
|
+
const entries = [];
|
|
20487
|
+
for (let index = 0; index < count; index += 1) {
|
|
20488
|
+
const nameOrId = table.readUInt32LE(index * 8);
|
|
20489
|
+
const target = table.readUInt32LE(index * 8 + 4);
|
|
20490
|
+
entries.push({
|
|
20491
|
+
id: nameOrId & 2147483647,
|
|
20492
|
+
named: (nameOrId & 2147483648) !== 0,
|
|
20493
|
+
isDirectory: (target & 2147483648) !== 0,
|
|
20494
|
+
target: target & 2147483647
|
|
20495
|
+
});
|
|
20496
|
+
}
|
|
20497
|
+
return entries;
|
|
20498
|
+
};
|
|
20499
|
+
const root = readDirectory(0);
|
|
20500
|
+
const typeEntry = root?.find((entry) => !entry.named && entry.id === 16 && entry.isDirectory);
|
|
20501
|
+
if (!typeEntry) return void 0;
|
|
20502
|
+
let level = readDirectory(typeEntry.target);
|
|
20503
|
+
let depth = 1;
|
|
20504
|
+
let leaf;
|
|
20505
|
+
while (level && level.length > 0 && depth <= RESOURCE_MAX_DEPTH) {
|
|
20506
|
+
const entry = level[0];
|
|
20507
|
+
if (!entry.isDirectory) {
|
|
20508
|
+
leaf = entry;
|
|
20509
|
+
break;
|
|
20510
|
+
}
|
|
20511
|
+
level = readDirectory(entry.target);
|
|
20512
|
+
depth += 1;
|
|
20513
|
+
}
|
|
20514
|
+
if (!leaf) return void 0;
|
|
20515
|
+
const dataEntry = readRva(base + leaf.target, 16);
|
|
20516
|
+
if (!dataEntry) return void 0;
|
|
20517
|
+
const dataRva = dataEntry.readUInt32LE(0);
|
|
20518
|
+
const dataSize = dataEntry.readUInt32LE(4);
|
|
20519
|
+
if (dataSize < 8 || dataSize > 256 * 1024) {
|
|
20520
|
+
warnings.push("The version resource has an unexpected size and was skipped.");
|
|
20521
|
+
return void 0;
|
|
20522
|
+
}
|
|
20523
|
+
const offset = rvaToOffset(dataRva);
|
|
20524
|
+
const data = offset === void 0 ? void 0 : reader.read(offset, dataSize);
|
|
20525
|
+
if (!data) return void 0;
|
|
20526
|
+
return parseVersionResource(data);
|
|
20527
|
+
}
|
|
20528
|
+
function align4(value) {
|
|
20529
|
+
return value + 3 & ~3;
|
|
20530
|
+
}
|
|
20531
|
+
function readVersionBlock(buffer, offset, limit) {
|
|
20532
|
+
if (offset + 6 > limit) return void 0;
|
|
20533
|
+
const length = buffer.readUInt16LE(offset);
|
|
20534
|
+
const valueLength = buffer.readUInt16LE(offset + 2);
|
|
20535
|
+
const type = buffer.readUInt16LE(offset + 4);
|
|
20536
|
+
if (length < 6 || offset + length > limit) return void 0;
|
|
20537
|
+
let cursor = offset + 6;
|
|
20538
|
+
let key = "";
|
|
20539
|
+
while (cursor + 2 <= offset + length) {
|
|
20540
|
+
const unit = buffer.readUInt16LE(cursor);
|
|
20541
|
+
cursor += 2;
|
|
20542
|
+
if (unit === 0) break;
|
|
20543
|
+
key += String.fromCharCode(unit);
|
|
20544
|
+
if (key.length > 64) return void 0;
|
|
20545
|
+
}
|
|
20546
|
+
const valueOffset = align4(cursor);
|
|
20547
|
+
const valueBytes = type === 1 ? valueLength * 2 : valueLength;
|
|
20548
|
+
return {
|
|
20549
|
+
key,
|
|
20550
|
+
valueOffset,
|
|
20551
|
+
valueLength,
|
|
20552
|
+
type,
|
|
20553
|
+
childrenOffset: align4(valueOffset + valueBytes),
|
|
20554
|
+
end: offset + length
|
|
20555
|
+
};
|
|
20556
|
+
}
|
|
20557
|
+
var VERSION_FIELDS = {
|
|
20558
|
+
CompanyName: "companyName",
|
|
20559
|
+
FileDescription: "fileDescription",
|
|
20560
|
+
FileVersion: "fileVersion",
|
|
20561
|
+
InternalName: "internalName",
|
|
20562
|
+
LegalCopyright: "legalCopyright",
|
|
20563
|
+
OriginalFilename: "originalFilename",
|
|
20564
|
+
ProductName: "productName",
|
|
20565
|
+
ProductVersion: "productVersion"
|
|
20566
|
+
};
|
|
20567
|
+
function parseVersionResource(data) {
|
|
20568
|
+
const root = readVersionBlock(data, 0, data.length);
|
|
20569
|
+
if (!root || root.key !== "VS_VERSION_INFO") return void 0;
|
|
20570
|
+
const result = {};
|
|
20571
|
+
let cursor = root.childrenOffset;
|
|
20572
|
+
while (cursor < root.end) {
|
|
20573
|
+
const group = readVersionBlock(data, cursor, root.end);
|
|
20574
|
+
if (!group) break;
|
|
20575
|
+
if (group.key === "StringFileInfo") {
|
|
20576
|
+
let tableCursor = group.childrenOffset;
|
|
20577
|
+
const table = readVersionBlock(data, tableCursor, group.end);
|
|
20578
|
+
if (table) {
|
|
20579
|
+
tableCursor = table.childrenOffset;
|
|
20580
|
+
while (tableCursor < table.end) {
|
|
20581
|
+
const entry = readVersionBlock(data, tableCursor, table.end);
|
|
20582
|
+
if (!entry) break;
|
|
20583
|
+
const field = VERSION_FIELDS[entry.key];
|
|
20584
|
+
if (field && entry.type === 1 && entry.valueLength > 0) {
|
|
20585
|
+
const raw = data.toString(
|
|
20586
|
+
"utf16le",
|
|
20587
|
+
entry.valueOffset,
|
|
20588
|
+
Math.min(entry.valueOffset + entry.valueLength * 2, entry.end)
|
|
20589
|
+
);
|
|
20590
|
+
const value = raw.replace(/\0+$/, "").trim();
|
|
20591
|
+
if (value) result[field] = value.slice(0, 256);
|
|
20592
|
+
}
|
|
20593
|
+
tableCursor = align4(entry.end);
|
|
20594
|
+
}
|
|
20595
|
+
}
|
|
20596
|
+
}
|
|
20597
|
+
cursor = align4(group.end);
|
|
20598
|
+
}
|
|
20599
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
20600
|
+
}
|
|
20601
|
+
|
|
20602
|
+
// src/inspectors/windowsApp.ts
|
|
20603
|
+
var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
|
|
20604
|
+
var WALK_ENTRY_LIMIT = 2e4;
|
|
20605
|
+
var WALK_DEPTH_LIMIT = 4;
|
|
20606
|
+
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;
|
|
20607
|
+
function normalizeName(value) {
|
|
20608
|
+
return value.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
20609
|
+
}
|
|
20610
|
+
function relative(root, target) {
|
|
20611
|
+
const value = import_node_path4.default.relative(root, target).split(import_node_path4.default.sep).join("/");
|
|
20612
|
+
return value === "" ? "." : value;
|
|
20613
|
+
}
|
|
20614
|
+
function walk(root) {
|
|
20615
|
+
const files = [];
|
|
20616
|
+
const directories = [];
|
|
20617
|
+
let seen = 0;
|
|
20618
|
+
let truncated = false;
|
|
20619
|
+
const visit = (dir, depth) => {
|
|
20620
|
+
let entries;
|
|
20621
|
+
try {
|
|
20622
|
+
entries = import_node_fs6.default.readdirSync(dir, { withFileTypes: true });
|
|
20623
|
+
} catch {
|
|
20624
|
+
return;
|
|
20625
|
+
}
|
|
20626
|
+
for (const entry of entries) {
|
|
20627
|
+
if (seen >= WALK_ENTRY_LIMIT) {
|
|
20628
|
+
truncated = true;
|
|
20629
|
+
return;
|
|
20630
|
+
}
|
|
20631
|
+
seen += 1;
|
|
20632
|
+
if (entry.isSymbolicLink()) continue;
|
|
20633
|
+
const full = import_node_path4.default.join(dir, entry.name);
|
|
20634
|
+
const rel = relative(root, full);
|
|
20635
|
+
if (entry.isDirectory()) {
|
|
20636
|
+
directories.push(rel.toLowerCase());
|
|
20637
|
+
if (depth < WALK_DEPTH_LIMIT) visit(full, depth + 1);
|
|
20638
|
+
} else if (entry.isFile()) {
|
|
20639
|
+
let size = 0;
|
|
20640
|
+
try {
|
|
20641
|
+
size = import_node_fs6.default.statSync(full).size;
|
|
20642
|
+
} catch {
|
|
20643
|
+
}
|
|
20644
|
+
files.push({ relative: rel, lower: rel.toLowerCase(), size });
|
|
20645
|
+
}
|
|
20646
|
+
}
|
|
20647
|
+
};
|
|
20648
|
+
visit(root, 0);
|
|
20649
|
+
return { files, directories, truncated };
|
|
20650
|
+
}
|
|
20651
|
+
function topLevelExecutables(dir) {
|
|
20652
|
+
try {
|
|
20653
|
+
return import_node_fs6.default.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && /\.exe$/i.test(entry.name)).map((entry) => entry.name);
|
|
20654
|
+
} catch {
|
|
20655
|
+
return [];
|
|
20656
|
+
}
|
|
20657
|
+
}
|
|
20658
|
+
function newestSquirrelVersion(dir) {
|
|
20659
|
+
try {
|
|
20660
|
+
const versions = import_node_fs6.default.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && /^app-\d/i.test(entry.name)).map((entry) => entry.name);
|
|
20661
|
+
versions.sort(
|
|
20662
|
+
(a, b) => a.localeCompare(b, void 0, { numeric: true, sensitivity: "base" })
|
|
20663
|
+
);
|
|
20664
|
+
return versions.length ? versions[versions.length - 1] : void 0;
|
|
20665
|
+
} catch {
|
|
20666
|
+
return void 0;
|
|
20667
|
+
}
|
|
20668
|
+
}
|
|
20669
|
+
function findWindowsElectronRoot(appPath) {
|
|
20670
|
+
let base = appPath;
|
|
20671
|
+
try {
|
|
20672
|
+
if (import_node_fs6.default.statSync(appPath).isFile()) base = import_node_path4.default.dirname(appPath);
|
|
20673
|
+
} catch {
|
|
20674
|
+
return void 0;
|
|
20675
|
+
}
|
|
20676
|
+
const roots = [base];
|
|
20677
|
+
const squirrel = newestSquirrelVersion(base);
|
|
20678
|
+
if (squirrel) roots.push(import_node_path4.default.join(base, squirrel));
|
|
20679
|
+
for (const root of roots) {
|
|
20680
|
+
const asar = import_node_path4.default.join(root, "resources", "app.asar");
|
|
20681
|
+
if (import_node_fs6.default.existsSync(asar)) return asar;
|
|
20682
|
+
const unpacked = import_node_path4.default.join(root, "resources", "app");
|
|
20683
|
+
if (import_node_fs6.default.existsSync(import_node_path4.default.join(unpacked, "package.json"))) return unpacked;
|
|
20684
|
+
}
|
|
20685
|
+
return void 0;
|
|
20686
|
+
}
|
|
20687
|
+
function rankExecutables(root, names, appName) {
|
|
20688
|
+
const wanted = normalizeName(appName);
|
|
20689
|
+
const scored = names.map((name) => {
|
|
20690
|
+
let size = 0;
|
|
20691
|
+
try {
|
|
20692
|
+
size = import_node_fs6.default.statSync(import_node_path4.default.join(root, name)).size;
|
|
20693
|
+
} catch {
|
|
20694
|
+
}
|
|
20695
|
+
let score = Math.log10(size + 1);
|
|
20696
|
+
if (HELPER_EXECUTABLE.test(name)) score -= 100;
|
|
20697
|
+
if (wanted && normalizeName(name.replace(/\.exe$/i, "")) === wanted) score += 50;
|
|
20698
|
+
return { name, score };
|
|
20699
|
+
});
|
|
20700
|
+
scored.sort((a, b) => b.score - a.score || a.name.localeCompare(b.name));
|
|
20701
|
+
return scored.map((item) => item.name);
|
|
20702
|
+
}
|
|
20703
|
+
var FILE_MARKERS = [
|
|
20704
|
+
{ hint: "CEF (Chromium Embedded Framework)", test: (f) => /(^|\/)libcef\.dll$/.test(f), basis: "libcef.dll" },
|
|
20705
|
+
{ hint: "WebView2", test: (f) => /(^|\/)webview2loader\.dll$/.test(f), basis: "WebView2Loader.dll" },
|
|
20706
|
+
{ hint: "Qt", test: (f) => /(^|\/)qt\d?core(d)?\.dll$/.test(f), basis: "QtCore library" },
|
|
20707
|
+
{ hint: "Flutter", test: (f) => /(^|\/)flutter_windows\.dll$/.test(f), basis: "flutter_windows.dll" },
|
|
20708
|
+
{ hint: "Unity", test: (f) => /(^|\/)unityplayer\.dll$/.test(f), basis: "UnityPlayer.dll" },
|
|
20709
|
+
{ hint: "NW.js", test: (f) => /(^|\/)nw(_elf)?\.dll$/.test(f), basis: "nw.dll" },
|
|
20710
|
+
{ hint: "Java", test: (f) => /(^|\/)jvm\.dll$/.test(f), basis: "bundled jvm.dll" },
|
|
20711
|
+
{ hint: "Python", test: (f) => /(^|\/)python3\d*\.dll$/.test(f), basis: "python3 runtime DLL" },
|
|
20712
|
+
{ hint: "PyInstaller", test: (f) => /(^|\/)base_library\.zip$/.test(f), basis: "base_library.zip" },
|
|
20713
|
+
{ hint: ".NET (modern runtime)", test: (f) => /(^|\/)(hostfxr|coreclr)\.dll$/.test(f) || /\.runtimeconfig\.json$/.test(f), basis: "hostfxr/coreclr or runtimeconfig.json" },
|
|
20714
|
+
{ hint: "WPF", test: (f) => /(^|\/)presentationframework\.dll$/.test(f), basis: "PresentationFramework.dll" },
|
|
20715
|
+
{ hint: "Windows Forms", test: (f) => /(^|\/)system\.windows\.forms\.dll$/.test(f), basis: "System.Windows.Forms.dll" },
|
|
20716
|
+
{ hint: "WinUI", test: (f) => /(^|\/)microsoft\.ui\.xaml\.dll$/.test(f), basis: "Microsoft.UI.Xaml.dll" },
|
|
20717
|
+
{ hint: "Squirrel installer", test: (f) => f === "update.exe", basis: "Update.exe" },
|
|
20718
|
+
{ hint: "Inno Setup uninstaller", test: (f) => /(^|\/)unins\d+\.exe$/.test(f), basis: "unins000.exe" },
|
|
20719
|
+
{ hint: "NSIS uninstaller", test: (f) => /(^|\/)uninstall[^/]*\.exe$/.test(f), basis: "Uninstall*.exe" }
|
|
20720
|
+
];
|
|
20721
|
+
var BINARY_MARKERS = [
|
|
20722
|
+
{ hint: "Go", needle: Buffer.from("Go buildinf:", "latin1"), basis: "Go build info in the executable" },
|
|
20723
|
+
{ hint: "Rust", needle: Buffer.from("/rustc/", "latin1"), basis: "rustc source paths in the executable" },
|
|
20724
|
+
{ hint: "Tauri", needle: Buffer.from("tauri-runtime", "latin1"), basis: "tauri-runtime crate paths in the executable" },
|
|
20725
|
+
{ hint: "Wails", needle: Buffer.from("wailsapp/wails", "latin1"), basis: "wails module path in the executable" },
|
|
20726
|
+
{ hint: "PyInstaller", needle: Buffer.from("PyInstaller", "latin1"), basis: "PyInstaller bootloader strings" },
|
|
20727
|
+
{ hint: "Electron", needle: Buffer.from("dL7pKGdnNz796PbbjQWNKmHXBZaB9tsX", "latin1"), basis: "Electron fuse sentinel in the executable" }
|
|
20728
|
+
];
|
|
20729
|
+
var BINARY_SCAN_LIMIT = 192 * 1024 * 1024;
|
|
20730
|
+
var BINARY_SCAN_CHUNK = 8 * 1024 * 1024;
|
|
20731
|
+
function scanBinaryMarkers(filePath) {
|
|
20732
|
+
const found = [];
|
|
20733
|
+
let fd;
|
|
20734
|
+
try {
|
|
20735
|
+
fd = import_node_fs6.default.openSync(filePath, "r");
|
|
20736
|
+
const size = import_node_fs6.default.fstatSync(fd).size;
|
|
20737
|
+
const overlap = Math.max(...BINARY_MARKERS.map((marker) => marker.needle.length));
|
|
20738
|
+
const buffer = Buffer.alloc(BINARY_SCAN_CHUNK + overlap);
|
|
20739
|
+
let position = 0;
|
|
20740
|
+
let carried = 0;
|
|
20741
|
+
while (position < Math.min(size, BINARY_SCAN_LIMIT)) {
|
|
20742
|
+
const read = import_node_fs6.default.readSync(fd, buffer, carried, BINARY_SCAN_CHUNK, position);
|
|
20743
|
+
if (read <= 0) break;
|
|
20744
|
+
const view = buffer.subarray(0, carried + read);
|
|
20745
|
+
for (const marker of BINARY_MARKERS) {
|
|
20746
|
+
if (!found.some((item) => item.hint === marker.hint) && view.includes(marker.needle)) {
|
|
20747
|
+
found.push({ hint: marker.hint, basis: marker.basis });
|
|
20748
|
+
}
|
|
20749
|
+
}
|
|
20750
|
+
position += read;
|
|
20751
|
+
carried = Math.min(overlap - 1, view.length);
|
|
20752
|
+
view.copy(buffer, 0, view.length - carried);
|
|
20753
|
+
}
|
|
20754
|
+
} catch {
|
|
20755
|
+
} finally {
|
|
20756
|
+
if (fd !== void 0) import_node_fs6.default.closeSync(fd);
|
|
20757
|
+
}
|
|
20758
|
+
return found;
|
|
20759
|
+
}
|
|
20760
|
+
function detectTechnology(files, directories, electron, pe, mainPath) {
|
|
20761
|
+
const evidence = [];
|
|
20762
|
+
const add = (hint, basis) => {
|
|
20763
|
+
if (!evidence.some((item) => item.hint === hint)) evidence.push({ hint, basis });
|
|
20764
|
+
};
|
|
20765
|
+
if (electron.asar) add("Electron", "resources/app.asar");
|
|
20766
|
+
else if (electron.unpackedApp) add("Electron", "resources/app");
|
|
20767
|
+
const lower = files.map((file) => file.lower);
|
|
20768
|
+
if (!electron.asar && !electron.unpackedApp && lower.includes("chrome_100_percent.pak") && lower.includes("resources.pak")) {
|
|
20769
|
+
add("Chromium-based", "chrome_100_percent.pak and resources.pak");
|
|
20770
|
+
}
|
|
20771
|
+
for (const marker of FILE_MARKERS) {
|
|
20772
|
+
if (lower.some(marker.test)) add(marker.hint, marker.basis);
|
|
20773
|
+
}
|
|
20774
|
+
if (directories.some((dir) => /(^|\/)[^/]+_data$/.test(dir)) && lower.some((f) => /(^|\/)unityplayer\.dll$/.test(f))) {
|
|
20775
|
+
add("Unity", "*_Data directory");
|
|
20776
|
+
}
|
|
20777
|
+
if (pe?.dotnet) {
|
|
20778
|
+
add(".NET", pe.dotnet.metadataVersion ? `CLR header (${pe.dotnet.metadataVersion})` : "CLR header");
|
|
20779
|
+
}
|
|
20780
|
+
if (pe?.imports.libraries.some((library) => /^mscoree\.dll$/i.test(library.name))) {
|
|
20781
|
+
add(".NET Framework", "imports mscoree.dll");
|
|
20782
|
+
}
|
|
20783
|
+
const libraries = pe?.imports.libraries.map((library) => library.name.toLowerCase()) ?? [];
|
|
20784
|
+
if (libraries.some((name) => /^mfc\d+/.test(name))) add("MFC", "imports mfc*.dll");
|
|
20785
|
+
if (libraries.some((name) => /^qt\d?(core|gui|widgets)/.test(name))) add("Qt", "imports Qt libraries");
|
|
20786
|
+
if (libraries.some((name) => /^webview2loader\.dll$/.test(name))) add("WebView2", "imports WebView2Loader.dll");
|
|
20787
|
+
if (mainPath) {
|
|
20788
|
+
for (const found of scanBinaryMarkers(mainPath)) add(found.hint, found.basis);
|
|
20789
|
+
}
|
|
20790
|
+
return { hints: evidence.map((item) => item.hint), evidence };
|
|
20791
|
+
}
|
|
20792
|
+
function powershellPath() {
|
|
20793
|
+
const root = process.env.SystemRoot || "C:\\Windows";
|
|
20794
|
+
return import_node_path4.default.join(root, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
20795
|
+
}
|
|
20796
|
+
async function verifyAuthenticode(filePath, warnings) {
|
|
20797
|
+
if (process.platform !== "win32") {
|
|
20798
|
+
warnings.push("Authenticode verification requires a Windows host; only the embedded signature block was read.");
|
|
20799
|
+
return void 0;
|
|
20800
|
+
}
|
|
20801
|
+
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";
|
|
20802
|
+
try {
|
|
20803
|
+
const { stdout } = await execFileAsync2(
|
|
20804
|
+
powershellPath(),
|
|
20805
|
+
["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", script],
|
|
20806
|
+
{
|
|
20807
|
+
timeout: 45e3,
|
|
20808
|
+
maxBuffer: 1024 * 1024,
|
|
20809
|
+
windowsHide: true,
|
|
20810
|
+
env: { ...process.env, APPTRACE_SIGNATURE_PATH: filePath }
|
|
20811
|
+
}
|
|
20812
|
+
);
|
|
20813
|
+
const parsed = JSON.parse(stdout.trim());
|
|
20814
|
+
const text2 = (value) => typeof value === "string" && value ? value : void 0;
|
|
20815
|
+
const status = text2(parsed.Status);
|
|
20816
|
+
return {
|
|
20817
|
+
verified: status === "Valid",
|
|
20818
|
+
status,
|
|
20819
|
+
signer: text2(parsed.Subject),
|
|
20820
|
+
issuer: text2(parsed.Issuer),
|
|
20821
|
+
thumbprint: text2(parsed.Thumbprint),
|
|
20822
|
+
notAfter: text2(parsed.NotAfter),
|
|
20823
|
+
timestamper: text2(parsed.Timestamper)
|
|
20824
|
+
};
|
|
20825
|
+
} catch (error) {
|
|
20826
|
+
warnings.push(
|
|
20827
|
+
`Authenticode verification did not complete: ${error instanceof Error ? error.message.split("\n")[0] : String(error)}`
|
|
20828
|
+
);
|
|
20829
|
+
return void 0;
|
|
20830
|
+
}
|
|
20831
|
+
}
|
|
20832
|
+
function packerWarnings(pe, warnings) {
|
|
20833
|
+
const packerNames = /^(upx\d|\.upx|\.aspack|\.adata|\.themida|\.vmp\d|\.petite|\.mpress|\.enigma\d|\.nsp\d|\.packed)/i;
|
|
20834
|
+
for (const section of pe.sections) {
|
|
20835
|
+
if (packerNames.test(section.name)) {
|
|
20836
|
+
warnings.push(`Section "${section.name}" is typical of a packer; static results may cover only the unpacker stub.`);
|
|
20837
|
+
return;
|
|
20838
|
+
}
|
|
20839
|
+
}
|
|
20840
|
+
const dense = pe.sections.filter((section) => section.executable && (section.entropy ?? 0) >= 7.4 && section.rawSize > 32 * 1024);
|
|
20841
|
+
if (dense.length > 0) {
|
|
20842
|
+
warnings.push(
|
|
20843
|
+
`Executable section "${dense[0].name}" has entropy ${dense[0].entropy}; the code may be packed or encrypted.`
|
|
20844
|
+
);
|
|
20845
|
+
}
|
|
20846
|
+
}
|
|
20847
|
+
async function inspectWindowsApp(appPath) {
|
|
20848
|
+
const warnings = [];
|
|
20849
|
+
const stat = import_node_fs6.default.statSync(appPath);
|
|
20850
|
+
let appRoot = stat.isDirectory() ? appPath : import_node_path4.default.dirname(appPath);
|
|
20851
|
+
const requestedExecutable = stat.isFile() ? import_node_path4.default.basename(appPath) : void 0;
|
|
20852
|
+
const appName = import_node_path4.default.basename(appRoot);
|
|
20853
|
+
let executables = requestedExecutable ? [requestedExecutable] : topLevelExecutables(appRoot);
|
|
20854
|
+
let ranked = rankExecutables(appRoot, executables, appName);
|
|
20855
|
+
const onlyHelpers = ranked.length > 0 && ranked.every((name) => HELPER_EXECUTABLE.test(name));
|
|
20856
|
+
if (!requestedExecutable && (ranked.length === 0 || onlyHelpers)) {
|
|
20857
|
+
const version2 = newestSquirrelVersion(appRoot);
|
|
20858
|
+
if (version2) {
|
|
20859
|
+
const nested = import_node_path4.default.join(appRoot, version2);
|
|
20860
|
+
const nestedRanked = rankExecutables(nested, topLevelExecutables(nested), appName);
|
|
20861
|
+
if (nestedRanked.length > 0) {
|
|
20862
|
+
warnings.push(`The program lives in "${version2}"; the newest versioned folder was inspected.`);
|
|
20863
|
+
appRoot = nested;
|
|
20864
|
+
executables = topLevelExecutables(nested);
|
|
20865
|
+
ranked = nestedRanked;
|
|
20866
|
+
}
|
|
20867
|
+
}
|
|
20868
|
+
}
|
|
20869
|
+
if (ranked.length === 0) {
|
|
20870
|
+
throw new Error("No Windows executable (.exe) was found at the top level of the application directory.");
|
|
20871
|
+
}
|
|
20872
|
+
const mainName = ranked[0];
|
|
20873
|
+
const mainPath = import_node_path4.default.join(appRoot, mainName);
|
|
20874
|
+
if (!isPeFile(mainPath)) {
|
|
20875
|
+
throw new Error(`"${mainName}" is not a Windows executable (the MZ signature is missing).`);
|
|
20876
|
+
}
|
|
20877
|
+
if (HELPER_EXECUTABLE.test(mainName)) {
|
|
20878
|
+
warnings.push(`Only helper-style executables were found; "${mainName}" was inspected but may not be the application itself.`);
|
|
20879
|
+
}
|
|
20880
|
+
if (ranked.length > 1) {
|
|
20881
|
+
const second = ranked[1];
|
|
20882
|
+
if (!HELPER_EXECUTABLE.test(second) && !requestedExecutable) {
|
|
20883
|
+
warnings.push(`Several executables could be the main program (${ranked.slice(0, 4).join(", ")}); "${mainName}" was chosen.`);
|
|
20884
|
+
}
|
|
20885
|
+
}
|
|
20886
|
+
const pe = inspectPeFile(mainPath);
|
|
20887
|
+
warnings.push(...pe.warnings);
|
|
20888
|
+
packerWarnings(pe, warnings);
|
|
20889
|
+
const walked = walk(appRoot);
|
|
20890
|
+
const asar = import_node_path4.default.join(appRoot, "resources", "app.asar");
|
|
20891
|
+
const unpacked = import_node_path4.default.join(appRoot, "resources", "app");
|
|
20892
|
+
let asarSize;
|
|
20893
|
+
try {
|
|
20894
|
+
asarSize = import_node_fs6.default.statSync(asar).size;
|
|
20895
|
+
} catch {
|
|
20896
|
+
}
|
|
20897
|
+
const electron = {
|
|
20898
|
+
asar: asarSize !== void 0,
|
|
20899
|
+
unpackedApp: import_node_fs6.default.existsSync(import_node_path4.default.join(unpacked, "package.json")),
|
|
20900
|
+
...asarSize !== void 0 ? { asarSizeBytes: asarSize } : {}
|
|
20901
|
+
};
|
|
20902
|
+
let topLevel = [];
|
|
20903
|
+
try {
|
|
20904
|
+
topLevel = import_node_fs6.default.readdirSync(appRoot).sort((a, b) => a.localeCompare(b)).slice(0, 200);
|
|
20905
|
+
} catch {
|
|
20906
|
+
}
|
|
20907
|
+
const signature = { embedded: pe.certificate.present };
|
|
20908
|
+
const verified = await verifyAuthenticode(mainPath, warnings);
|
|
20909
|
+
if (verified) Object.assign(signature, verified);
|
|
20910
|
+
if (!pe.certificate.present && (!verified || verified.status === "NotSigned")) {
|
|
20911
|
+
warnings.push("The main executable carries no Authenticode signature.");
|
|
20912
|
+
}
|
|
20913
|
+
const version = pe.version?.productVersion ?? pe.version?.fileVersion;
|
|
20914
|
+
return {
|
|
20915
|
+
kind: "windows-app",
|
|
20916
|
+
app: {
|
|
20917
|
+
path: appPath,
|
|
20918
|
+
name: pe.version?.productName ?? pe.version?.fileDescription ?? appName,
|
|
20919
|
+
version,
|
|
20920
|
+
publisher: pe.version?.companyName
|
|
20921
|
+
},
|
|
20922
|
+
layout: {
|
|
20923
|
+
root: relative(stat.isDirectory() ? appPath : import_node_path4.default.dirname(appPath), appRoot),
|
|
20924
|
+
topLevel,
|
|
20925
|
+
executableCount: walked.files.filter((file) => file.lower.endsWith(".exe")).length,
|
|
20926
|
+
dllCount: walked.files.filter((file) => file.lower.endsWith(".dll")).length,
|
|
20927
|
+
fileCount: walked.files.length,
|
|
20928
|
+
truncated: walked.truncated,
|
|
20929
|
+
electron
|
|
20930
|
+
},
|
|
20931
|
+
mainExecutable: {
|
|
20932
|
+
path: relative(stat.isDirectory() ? appPath : import_node_path4.default.dirname(appPath), mainPath),
|
|
20933
|
+
sizeBytes: pe.sizeBytes,
|
|
20934
|
+
candidates: ranked.slice(0, 8),
|
|
20935
|
+
pe
|
|
20936
|
+
},
|
|
20937
|
+
technology: detectTechnology(walked.files, walked.directories, electron, pe, mainPath),
|
|
20938
|
+
signature,
|
|
20939
|
+
warnings
|
|
20940
|
+
};
|
|
20941
|
+
}
|
|
20942
|
+
|
|
20077
20943
|
// src/inspectors/report.ts
|
|
20078
20944
|
function cell(value) {
|
|
20079
20945
|
return String(value ?? "\u2014").replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
|
|
@@ -20128,6 +20994,42 @@ function renderMacAppInspection(lines, inspection, warnings) {
|
|
|
20128
20994
|
lines.push("");
|
|
20129
20995
|
}
|
|
20130
20996
|
}
|
|
20997
|
+
function renderWindowsAppInspection(lines, inspection, warnings) {
|
|
20998
|
+
const app = record(inspection.app) ?? {};
|
|
20999
|
+
const layout = record(inspection.layout) ?? {};
|
|
21000
|
+
const electron = record(layout.electron);
|
|
21001
|
+
const main = record(inspection.mainExecutable);
|
|
21002
|
+
const pe = record(main?.pe);
|
|
21003
|
+
const imports = record(pe?.imports);
|
|
21004
|
+
const dotnet = record(pe?.dotnet);
|
|
21005
|
+
const signature = record(inspection.signature);
|
|
21006
|
+
const technology = record(inspection.technology);
|
|
21007
|
+
const hints = Array.isArray(technology?.hints) ? technology.hints.map(String) : [];
|
|
21008
|
+
lines.push(
|
|
21009
|
+
"| Field | Value |",
|
|
21010
|
+
"| --- | --- |",
|
|
21011
|
+
`| Name | ${cell(app.name)} |`,
|
|
21012
|
+
`| Version | ${cell(app.version)} |`,
|
|
21013
|
+
`| Publisher | ${cell(app.publisher)} |`,
|
|
21014
|
+
`| Main executable | ${cell(main?.path)} |`,
|
|
21015
|
+
`| Architecture | ${cell(pe?.architecture)} (${cell(pe?.format)}) |`,
|
|
21016
|
+
`| Subsystem | ${cell(pe?.subsystem)} |`,
|
|
21017
|
+
`| Imported libraries | ${cell(imports?.libraryCount)} |`,
|
|
21018
|
+
`| .NET runtime | ${cell(dotnet ? dotnet.runtimeVersion ?? "managed" : void 0)} |`,
|
|
21019
|
+
`| Executables / DLLs | ${cell(layout.executableCount)} / ${cell(layout.dllCount)} |`,
|
|
21020
|
+
`| Electron bundle | ${cell(electron?.asar ? "app.asar" : electron?.unpackedApp ? "unpacked app" : "none")} |`,
|
|
21021
|
+
`| Technology hints | ${cell(hints.length > 0 ? hints.join(", ") : void 0)} |`,
|
|
21022
|
+
`| Signature embedded | ${cell(signature?.embedded)} |`,
|
|
21023
|
+
`| Signature verified | ${cell(signature?.verified ?? "not checked on this host")} |`,
|
|
21024
|
+
`| Signer | ${cell(signature?.signer)} |`,
|
|
21025
|
+
""
|
|
21026
|
+
);
|
|
21027
|
+
if (warnings.length > 0) {
|
|
21028
|
+
lines.push("### Inspection warnings", "");
|
|
21029
|
+
for (const warning of warnings) lines.push(`- ${warning}`);
|
|
21030
|
+
lines.push("");
|
|
21031
|
+
}
|
|
21032
|
+
}
|
|
20131
21033
|
function renderIpaInspection(lines, inspection, warnings) {
|
|
20132
21034
|
lines.push(
|
|
20133
21035
|
"| Field | Value |",
|
|
@@ -20219,6 +21121,8 @@ function renderReportMarkdown(input2) {
|
|
|
20219
21121
|
lines.push(`Inspection failed: ${cell(inspection.error)}`, "");
|
|
20220
21122
|
} else if (inspection.kind === "mac-app") {
|
|
20221
21123
|
renderMacAppInspection(lines, inspection, warnings);
|
|
21124
|
+
} else if (inspection.kind === "windows-app") {
|
|
21125
|
+
renderWindowsAppInspection(lines, inspection, warnings);
|
|
20222
21126
|
} else {
|
|
20223
21127
|
renderIpaInspection(lines, inspection, warnings);
|
|
20224
21128
|
}
|
|
@@ -20261,6 +21165,12 @@ function renderReportMarkdown(input2) {
|
|
|
20261
21165
|
}
|
|
20262
21166
|
lines.push("");
|
|
20263
21167
|
}
|
|
21168
|
+
const analysisGaps = findings.filter((finding) => finding.type === "limitation").slice(0, 20);
|
|
21169
|
+
if (analysisGaps.length > 0) {
|
|
21170
|
+
lines.push("### Analysis gaps", "");
|
|
21171
|
+
for (const gap of analysisGaps) lines.push(`- ${gap.value}`);
|
|
21172
|
+
lines.push("");
|
|
21173
|
+
}
|
|
20264
21174
|
lines.push(
|
|
20265
21175
|
"## Jobs and evidence",
|
|
20266
21176
|
"",
|
|
@@ -20305,8 +21215,8 @@ function describeResult(result) {
|
|
|
20305
21215
|
}
|
|
20306
21216
|
|
|
20307
21217
|
// src/process/run.ts
|
|
20308
|
-
var
|
|
20309
|
-
var
|
|
21218
|
+
var import_node_child_process3 = require("child_process");
|
|
21219
|
+
var import_node_path5 = __toESM(require("path"));
|
|
20310
21220
|
function quoteWindowsArgument(value) {
|
|
20311
21221
|
if (value.length === 0) return '""';
|
|
20312
21222
|
if (!/[\s"&|<>^()%!]/.test(value)) return value;
|
|
@@ -20316,7 +21226,7 @@ function windowsCommandLine(command, args) {
|
|
|
20316
21226
|
return [command, ...args].map(quoteWindowsArgument).join(" ");
|
|
20317
21227
|
}
|
|
20318
21228
|
function windowsSpawnSpec(command, args) {
|
|
20319
|
-
const extension =
|
|
21229
|
+
const extension = import_node_path5.default.extname(command).toLowerCase();
|
|
20320
21230
|
if (extension !== ".cmd" && extension !== ".bat") {
|
|
20321
21231
|
return { command, args, verbatim: false };
|
|
20322
21232
|
}
|
|
@@ -20334,7 +21244,7 @@ function runCommand(command, args, options = {}) {
|
|
|
20334
21244
|
let child;
|
|
20335
21245
|
try {
|
|
20336
21246
|
const spec = process.platform === "win32" ? windowsSpawnSpec(command, args) : { command, args, verbatim: false };
|
|
20337
|
-
child = (0,
|
|
21247
|
+
child = (0, import_node_child_process3.spawn)(spec.command, spec.args, {
|
|
20338
21248
|
env: options.env ?? process.env,
|
|
20339
21249
|
cwd: options.cwd,
|
|
20340
21250
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -20458,7 +21368,7 @@ function uniqueStrings(values2, limit) {
|
|
|
20458
21368
|
function isNetworkEndpointValue(value) {
|
|
20459
21369
|
return /^(?:https?|wss?):\/\//i.test(value) || /^\/\//.test(value) || /^[a-z0-9.-]+\.[a-z]{2,}(?:[/:?#]|$)/i.test(value) || /^\/[a-z0-9._~!$&'()*+,;=:@%/-]+$/i.test(value);
|
|
20460
21370
|
}
|
|
20461
|
-
function
|
|
21371
|
+
function detectTechnology2(subjectName, modulePaths, assetPaths) {
|
|
20462
21372
|
const text2 = [subjectName, ...modulePaths, ...assetPaths].filter((value) => typeof value === "string").join("\n").toLowerCase();
|
|
20463
21373
|
const hints = [];
|
|
20464
21374
|
if (/__uni__|uni-app|uni-jsframework|hbuilder/.test(text2)) {
|
|
@@ -20510,6 +21420,13 @@ function javascriptFindings(job, artifact, evidenceId, parsed, summary) {
|
|
|
20510
21420
|
}
|
|
20511
21421
|
}
|
|
20512
21422
|
}
|
|
21423
|
+
if (artifact.packageFormat === "app" || artifact.packageFormat === "exe") {
|
|
21424
|
+
push("technology", "Electron", {
|
|
21425
|
+
group: "technology",
|
|
21426
|
+
confidence: "high",
|
|
21427
|
+
metadata: { basis: "electron-bundle" }
|
|
21428
|
+
});
|
|
21429
|
+
}
|
|
20513
21430
|
const normalized = record2(parsed.normalized_result);
|
|
20514
21431
|
const graph = record2(normalized?.graph);
|
|
20515
21432
|
const nodes = Array.isArray(graph?.nodes) ? graph.nodes : [];
|
|
@@ -20570,17 +21487,47 @@ function javascriptFindings(job, artifact, evidenceId, parsed, summary) {
|
|
|
20570
21487
|
}
|
|
20571
21488
|
}
|
|
20572
21489
|
const limitations = Array.isArray(parsed.limitations) ? parsed.limitations : [];
|
|
21490
|
+
const seenLimitations = /* @__PURE__ */ new Set();
|
|
21491
|
+
const LIMITATION_CAP = 50;
|
|
21492
|
+
let kept = 0;
|
|
20573
21493
|
for (const limitation of limitations) {
|
|
20574
|
-
if (typeof limitation
|
|
20575
|
-
|
|
21494
|
+
if (typeof limitation !== "string") continue;
|
|
21495
|
+
const normalized2 = limitation.replace(/\s+/g, " ").trim();
|
|
21496
|
+
if (!normalized2 || seenLimitations.has(normalized2)) continue;
|
|
21497
|
+
if (kept >= LIMITATION_CAP) break;
|
|
21498
|
+
seenLimitations.add(normalized2);
|
|
21499
|
+
kept += 1;
|
|
21500
|
+
push("limitation", normalized2, {
|
|
21501
|
+
group: "analysis-limitation",
|
|
21502
|
+
confidence: "exact",
|
|
21503
|
+
metadata: { evidenceId }
|
|
21504
|
+
});
|
|
21505
|
+
}
|
|
21506
|
+
if (limitations.length > kept) {
|
|
21507
|
+
push(
|
|
21508
|
+
"limitation",
|
|
21509
|
+
`${limitations.length - kept} additional per-file limitations were omitted from this summary.`,
|
|
21510
|
+
{
|
|
20576
21511
|
group: "analysis-limitation",
|
|
20577
21512
|
confidence: "exact",
|
|
20578
|
-
metadata: { evidenceId }
|
|
20579
|
-
}
|
|
20580
|
-
|
|
21513
|
+
metadata: { evidenceId, omitted: limitations.length - kept }
|
|
21514
|
+
}
|
|
21515
|
+
);
|
|
20581
21516
|
}
|
|
20582
21517
|
return findings;
|
|
20583
21518
|
}
|
|
21519
|
+
function findElectronJavascriptRoot(appPath) {
|
|
21520
|
+
const asar = import_node_path6.default.join(appPath, "Contents", "Resources", "app.asar");
|
|
21521
|
+
if (import_node_fs7.default.existsSync(asar)) return asar;
|
|
21522
|
+
const unpacked = import_node_path6.default.join(appPath, "Contents", "Resources", "app");
|
|
21523
|
+
if (import_node_fs7.default.existsSync(unpacked) && import_node_fs7.default.statSync(unpacked).isDirectory()) {
|
|
21524
|
+
return unpacked;
|
|
21525
|
+
}
|
|
21526
|
+
fail(
|
|
21527
|
+
"CAPABILITY_UNAVAILABLE",
|
|
21528
|
+
"No Electron JavaScript bundle (app.asar or Resources/app) was found in this app."
|
|
21529
|
+
);
|
|
21530
|
+
}
|
|
20584
21531
|
function nativeSearchFindings(job, artifact, evidenceId, parsed, kind) {
|
|
20585
21532
|
const values2 = Array.isArray(parsed.normalized_result) ? parsed.normalized_result : Array.isArray(parsed.raw_result) ? parsed.raw_result : [];
|
|
20586
21533
|
const createdAt = now();
|
|
@@ -20669,7 +21616,7 @@ function summarizeRea(parsed) {
|
|
|
20669
21616
|
operation: parsed.operation,
|
|
20670
21617
|
technology: {
|
|
20671
21618
|
confidence: "static-hint",
|
|
20672
|
-
hints:
|
|
21619
|
+
hints: detectTechnology2(subject?.name, modulePaths, assetPaths)
|
|
20673
21620
|
},
|
|
20674
21621
|
subject: subject ? {
|
|
20675
21622
|
name: subject.name,
|
|
@@ -20842,6 +21789,9 @@ var CapabilityJobRunner = class {
|
|
|
20842
21789
|
case "inspect:mac-app":
|
|
20843
21790
|
await this.runInspectMacApp(job, target, artifact);
|
|
20844
21791
|
return;
|
|
21792
|
+
case "inspect:pe":
|
|
21793
|
+
await this.runInspectPe(job, target, artifact);
|
|
21794
|
+
return;
|
|
20845
21795
|
case "extract:app":
|
|
20846
21796
|
await this.runExtract(job, target, artifact);
|
|
20847
21797
|
return;
|
|
@@ -20932,10 +21882,36 @@ var CapabilityJobRunner = class {
|
|
|
20932
21882
|
warnings: inspection.warnings.length
|
|
20933
21883
|
});
|
|
20934
21884
|
}
|
|
21885
|
+
async runInspectPe(job, target, artifact) {
|
|
21886
|
+
if (!artifact) fail("ARTIFACT_REQUIRED", "Acquire an artifact first.");
|
|
21887
|
+
this.log(job, "info", "Inspecting the Windows application.");
|
|
21888
|
+
this.patch(job, { progress: 25 });
|
|
21889
|
+
const inspection = await inspectWindowsApp(
|
|
21890
|
+
this.workspace.resolveStoredPath(artifact.path)
|
|
21891
|
+
);
|
|
21892
|
+
const pe = inspection.mainExecutable?.pe;
|
|
21893
|
+
const updated = {
|
|
21894
|
+
...artifact,
|
|
21895
|
+
version: inspection.app.version ?? artifact.version,
|
|
21896
|
+
architecture: pe?.architecture ?? artifact.architecture,
|
|
21897
|
+
inspection
|
|
21898
|
+
};
|
|
21899
|
+
this.store.insertArtifact(updated);
|
|
21900
|
+
this.workspace.saveArtifactMetadata(target, updated);
|
|
21901
|
+
this.patch(job, {
|
|
21902
|
+
status: "analyzed",
|
|
21903
|
+
progress: 100,
|
|
21904
|
+
result: { inspection },
|
|
21905
|
+
finishedAt: now()
|
|
21906
|
+
});
|
|
21907
|
+
this.log(job, "info", "Windows application inspection complete.", {
|
|
21908
|
+
warnings: inspection.warnings.length
|
|
21909
|
+
});
|
|
21910
|
+
}
|
|
20935
21911
|
async runExtract(job, target, artifact) {
|
|
20936
21912
|
if (!artifact) fail("ARTIFACT_REQUIRED", "\u8BF7\u5148\u83B7\u53D6 artifact\u3002");
|
|
20937
21913
|
this.log(job, "info", "\u5F00\u59CB\u89E3\u5305 .app\u3002");
|
|
20938
|
-
const outputDir =
|
|
21914
|
+
const outputDir = import_node_path6.default.join(
|
|
20939
21915
|
this.workspace.analysisDir(target),
|
|
20940
21916
|
"extract-app",
|
|
20941
21917
|
job.id
|
|
@@ -20949,7 +21925,7 @@ var CapabilityJobRunner = class {
|
|
|
20949
21925
|
progress: 100,
|
|
20950
21926
|
result: {
|
|
20951
21927
|
...extracted,
|
|
20952
|
-
outputDir:
|
|
21928
|
+
outputDir: import_node_path6.default.relative(this.workspace.root, extracted.outputDir)
|
|
20953
21929
|
},
|
|
20954
21930
|
finishedAt: now()
|
|
20955
21931
|
});
|
|
@@ -20959,31 +21935,71 @@ var CapabilityJobRunner = class {
|
|
|
20959
21935
|
}
|
|
20960
21936
|
async runReaJavascript(job, target, artifact) {
|
|
20961
21937
|
if (!artifact) fail("ARTIFACT_REQUIRED", "\u8BF7\u5148\u83B7\u53D6 artifact\u3002");
|
|
20962
|
-
|
|
20963
|
-
|
|
20964
|
-
|
|
20965
|
-
|
|
20966
|
-
|
|
21938
|
+
let javascriptRoot;
|
|
21939
|
+
if (artifact.packageFormat === "exe") {
|
|
21940
|
+
const found = findWindowsElectronRoot(
|
|
21941
|
+
this.workspace.resolveStoredPath(artifact.path)
|
|
21942
|
+
);
|
|
21943
|
+
if (!found) {
|
|
21944
|
+
fail(
|
|
21945
|
+
"CAPABILITY_UNAVAILABLE",
|
|
21946
|
+
"No Electron JavaScript bundle (resources/app.asar or resources/app) was found in this application."
|
|
21947
|
+
);
|
|
21948
|
+
}
|
|
21949
|
+
javascriptRoot = found;
|
|
21950
|
+
this.log(job, "info", "Running static analysis on the Windows JavaScript bundle.", {
|
|
21951
|
+
path: import_node_path6.default.relative(this.workspace.root, javascriptRoot)
|
|
21952
|
+
});
|
|
21953
|
+
this.patch(job, { progress: 25 });
|
|
21954
|
+
} else if (artifact.packageFormat === "app") {
|
|
21955
|
+
javascriptRoot = findElectronJavascriptRoot(
|
|
21956
|
+
this.workspace.resolveStoredPath(artifact.path)
|
|
21957
|
+
);
|
|
21958
|
+
this.log(job, "info", "Running static analysis on the macOS JavaScript bundle.", {
|
|
21959
|
+
path: import_node_path6.default.relative(this.workspace.root, javascriptRoot)
|
|
21960
|
+
});
|
|
21961
|
+
this.patch(job, { progress: 25 });
|
|
21962
|
+
} else {
|
|
21963
|
+
const extraction = await this.ensureExtraction(job, target, artifact);
|
|
21964
|
+
javascriptRoot = extraction.appRoot;
|
|
21965
|
+
this.log(job, "info", "\u5BF9\u89E3\u5305\u76EE\u5F55\u8FD0\u884C JavaScript \u9759\u6001\u5206\u6790\u3002", {
|
|
21966
|
+
path: import_node_path6.default.relative(this.workspace.root, javascriptRoot)
|
|
21967
|
+
});
|
|
21968
|
+
this.patch(job, { progress: 25 });
|
|
21969
|
+
}
|
|
20967
21970
|
const rea = await this.runReaJson(
|
|
20968
21971
|
job,
|
|
20969
21972
|
target,
|
|
20970
21973
|
"javascript-analyze",
|
|
20971
21974
|
[
|
|
20972
21975
|
"analyze-javascript-application",
|
|
20973
|
-
|
|
21976
|
+
javascriptRoot,
|
|
20974
21977
|
"--analysis-timeout-ms",
|
|
20975
|
-
"300000"
|
|
21978
|
+
"300000",
|
|
21979
|
+
// Signed Electron apps often carry unpacked native binaries whose
|
|
21980
|
+
// bytes no longer match the integrity recorded in app.asar. Keep
|
|
21981
|
+
// going and let REA mark those files untrusted rather than abort.
|
|
21982
|
+
...artifact.packageFormat === "ipa" ? [] : ["--integrity-policy", "record-and-continue"]
|
|
20976
21983
|
],
|
|
20977
|
-
|
|
21984
|
+
// Electron bundles are much larger than iOS web bundles; a 136 MB asar
|
|
21985
|
+
// takes several minutes on its own.
|
|
21986
|
+
15 * 6e4
|
|
20978
21987
|
);
|
|
20979
21988
|
const evidenceId = this.reaEvidenceId(rea);
|
|
21989
|
+
const summary = { ...rea.summary };
|
|
21990
|
+
if (artifact.packageFormat === "app" || artifact.packageFormat === "exe") {
|
|
21991
|
+
const technology = record2(summary.technology) ?? {};
|
|
21992
|
+
const hints = Array.isArray(technology.hints) ? [...technology.hints] : [];
|
|
21993
|
+
if (!hints.includes("Electron")) hints.unshift("Electron");
|
|
21994
|
+
summary.technology = { ...technology, hints };
|
|
21995
|
+
}
|
|
20980
21996
|
this.persistEvidence(
|
|
20981
21997
|
job,
|
|
20982
21998
|
artifact,
|
|
20983
21999
|
"javascript-analysis",
|
|
20984
22000
|
rea,
|
|
20985
22001
|
evidenceId,
|
|
20986
|
-
javascriptFindings(job, artifact, evidenceId, rea.parsed,
|
|
22002
|
+
javascriptFindings(job, artifact, evidenceId, rea.parsed, summary),
|
|
20987
22003
|
[
|
|
20988
22004
|
"technology",
|
|
20989
22005
|
"route",
|
|
@@ -20998,9 +22014,9 @@ var CapabilityJobRunner = class {
|
|
|
20998
22014
|
status: "analyzed",
|
|
20999
22015
|
progress: 100,
|
|
21000
22016
|
result: {
|
|
21001
|
-
...
|
|
22017
|
+
...summary,
|
|
21002
22018
|
evidenceId,
|
|
21003
|
-
outputDir:
|
|
22019
|
+
outputDir: import_node_path6.default.relative(this.workspace.root, javascriptRoot),
|
|
21004
22020
|
evidencePath: rea.rawPath
|
|
21005
22021
|
},
|
|
21006
22022
|
finishedAt: now()
|
|
@@ -21044,7 +22060,7 @@ var CapabilityJobRunner = class {
|
|
|
21044
22060
|
result: {
|
|
21045
22061
|
...rea.summary,
|
|
21046
22062
|
evidenceId,
|
|
21047
|
-
binaryPath:
|
|
22063
|
+
binaryPath: import_node_path6.default.relative(this.workspace.root, prepared.binaryPath),
|
|
21048
22064
|
evidencePath: rea.rawPath,
|
|
21049
22065
|
encryption,
|
|
21050
22066
|
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
|
|
@@ -21107,7 +22123,7 @@ var CapabilityJobRunner = class {
|
|
|
21107
22123
|
mode,
|
|
21108
22124
|
matchCount: rawMatches.length,
|
|
21109
22125
|
matches,
|
|
21110
|
-
binaryPath:
|
|
22126
|
+
binaryPath: import_node_path6.default.relative(this.workspace.root, prepared.binaryPath),
|
|
21111
22127
|
evidencePath: rea.rawPath,
|
|
21112
22128
|
limitations: Array.isArray(rea.parsed.limitations) ? rea.parsed.limitations : [],
|
|
21113
22129
|
encryption: encryptionState(prepared.inspection)
|
|
@@ -21152,7 +22168,7 @@ var CapabilityJobRunner = class {
|
|
|
21152
22168
|
result: {
|
|
21153
22169
|
...rea.summary,
|
|
21154
22170
|
functionName: input2.functionName,
|
|
21155
|
-
binaryPath:
|
|
22171
|
+
binaryPath: import_node_path6.default.relative(this.workspace.root, prepared.binaryPath),
|
|
21156
22172
|
evidencePath: rea.rawPath,
|
|
21157
22173
|
encryption
|
|
21158
22174
|
},
|
|
@@ -21167,26 +22183,44 @@ var CapabilityJobRunner = class {
|
|
|
21167
22183
|
this.log(job, "info", "\u6C47\u603B Target\u3001artifact \u548C\u4EFB\u52A1\u8BC1\u636E\u3002");
|
|
21168
22184
|
this.patch(job, { progress: 35 });
|
|
21169
22185
|
const generatedAt = now();
|
|
21170
|
-
const reportPath =
|
|
22186
|
+
const reportPath = import_node_path6.default.join(
|
|
21171
22187
|
this.workspace.targetDir(target),
|
|
21172
22188
|
"reports",
|
|
21173
22189
|
`${job.id}.md`
|
|
21174
22190
|
);
|
|
21175
|
-
const
|
|
22191
|
+
const findingTypes = [
|
|
22192
|
+
"technology",
|
|
22193
|
+
"route",
|
|
22194
|
+
"network_endpoint",
|
|
22195
|
+
"module",
|
|
22196
|
+
"asset",
|
|
22197
|
+
"storage",
|
|
22198
|
+
"native_procedure",
|
|
22199
|
+
"risk",
|
|
22200
|
+
"limitation"
|
|
22201
|
+
];
|
|
22202
|
+
const findings = findingTypes.flatMap(
|
|
22203
|
+
(type) => this.store.searchFindings({
|
|
22204
|
+
targetId: target.id,
|
|
22205
|
+
types: [type],
|
|
22206
|
+
limit: 20
|
|
22207
|
+
}).items
|
|
22208
|
+
);
|
|
22209
|
+
const findingTotal = this.store.searchFindings({
|
|
21176
22210
|
targetId: target.id,
|
|
21177
|
-
limit:
|
|
21178
|
-
});
|
|
22211
|
+
limit: 1
|
|
22212
|
+
}).total;
|
|
21179
22213
|
const markdown = renderReportMarkdown({
|
|
21180
22214
|
target,
|
|
21181
22215
|
artifact,
|
|
21182
22216
|
jobs: this.store.listJobs(target.id).filter((item) => item.id !== job.id).slice(0, 20),
|
|
21183
|
-
findings
|
|
21184
|
-
findingTotal
|
|
22217
|
+
findings,
|
|
22218
|
+
findingTotal,
|
|
21185
22219
|
hostPlatform: this.config.hostPlatform,
|
|
21186
22220
|
generatedAt
|
|
21187
22221
|
});
|
|
21188
22222
|
this.workspace.writeText(reportPath, markdown);
|
|
21189
|
-
const relativePath =
|
|
22223
|
+
const relativePath = import_node_path6.default.relative(this.workspace.root, reportPath);
|
|
21190
22224
|
this.patch(job, {
|
|
21191
22225
|
status: "analyzed",
|
|
21192
22226
|
progress: 100,
|
|
@@ -21218,12 +22252,12 @@ var CapabilityJobRunner = class {
|
|
|
21218
22252
|
fail("MAIN_EXECUTABLE_NOT_FOUND", "IPA \u68C0\u67E5\u7ED3\u679C\u4E2D\u6CA1\u6709\u4E3B\u7A0B\u5E8F\u8DEF\u5F84\u3002");
|
|
21219
22253
|
}
|
|
21220
22254
|
const extraction = await this.ensureExtraction(job, target, current);
|
|
21221
|
-
const binaryPath =
|
|
21222
|
-
const root =
|
|
21223
|
-
if (binaryPath !== root && !binaryPath.startsWith(root +
|
|
22255
|
+
const binaryPath = import_node_path6.default.resolve(extraction.outputDir, executable);
|
|
22256
|
+
const root = import_node_path6.default.resolve(extraction.outputDir);
|
|
22257
|
+
if (binaryPath !== root && !binaryPath.startsWith(root + import_node_path6.default.sep)) {
|
|
21224
22258
|
fail("UNSAFE_ARTIFACT_PATH", "\u4E3B\u7A0B\u5E8F\u8DEF\u5F84\u8D8A\u51FA\u89E3\u5305\u76EE\u5F55\uFF0C\u5DF2\u62D2\u7EDD\u6267\u884C\u3002");
|
|
21225
22259
|
}
|
|
21226
|
-
if (!
|
|
22260
|
+
if (!import_node_fs7.default.existsSync(binaryPath)) {
|
|
21227
22261
|
fail("MAIN_EXECUTABLE_NOT_FOUND", `\u89E3\u5305\u76EE\u5F55\u4E2D\u6CA1\u6709\u4E3B\u7A0B\u5E8F\uFF1A${executable}`);
|
|
21228
22262
|
}
|
|
21229
22263
|
return { inspection, binaryPath };
|
|
@@ -21232,24 +22266,24 @@ var CapabilityJobRunner = class {
|
|
|
21232
22266
|
const previous = this.findExtraction(target.id, artifact.id);
|
|
21233
22267
|
if (previous) {
|
|
21234
22268
|
this.log(job, "info", "\u590D\u7528\u5DF2\u6709\u89E3\u5305\u76EE\u5F55\u3002", {
|
|
21235
|
-
path:
|
|
22269
|
+
path: import_node_path6.default.relative(this.workspace.root, previous.outputDir)
|
|
21236
22270
|
});
|
|
21237
22271
|
return previous;
|
|
21238
22272
|
}
|
|
21239
|
-
const outputDir =
|
|
22273
|
+
const outputDir = import_node_path6.default.join(
|
|
21240
22274
|
this.workspace.analysisDir(target),
|
|
21241
22275
|
"extracted",
|
|
21242
22276
|
artifact.id
|
|
21243
22277
|
);
|
|
21244
|
-
const existingApp =
|
|
22278
|
+
const existingApp = import_node_fs7.default.existsSync(outputDir) ? findAppPrefix(outputDir) : void 0;
|
|
21245
22279
|
if (existingApp) {
|
|
21246
22280
|
const location = {
|
|
21247
22281
|
outputDir,
|
|
21248
22282
|
prefix: existingApp,
|
|
21249
|
-
appRoot:
|
|
22283
|
+
appRoot: import_node_path6.default.join(outputDir, existingApp)
|
|
21250
22284
|
};
|
|
21251
22285
|
this.log(job, "info", "\u590D\u7528\u6309 artifact \u54C8\u5E0C\u5F52\u6863\u7684\u89E3\u5305\u76EE\u5F55\u3002", {
|
|
21252
|
-
path:
|
|
22286
|
+
path: import_node_path6.default.relative(this.workspace.root, outputDir)
|
|
21253
22287
|
});
|
|
21254
22288
|
return location;
|
|
21255
22289
|
}
|
|
@@ -21259,8 +22293,8 @@ var CapabilityJobRunner = class {
|
|
|
21259
22293
|
this.workspace.resolveStoredPath(artifact.path),
|
|
21260
22294
|
outputDir
|
|
21261
22295
|
);
|
|
21262
|
-
const appRoot =
|
|
21263
|
-
if (appRoot !== outputDir && !appRoot.startsWith(outputDir +
|
|
22296
|
+
const appRoot = import_node_path6.default.resolve(outputDir, extracted.prefix);
|
|
22297
|
+
if (appRoot !== outputDir && !appRoot.startsWith(outputDir + import_node_path6.default.sep)) {
|
|
21264
22298
|
fail("UNSAFE_ARTIFACT_PATH", "\u89E3\u5305\u524D\u7F00\u8D8A\u51FA\u8F93\u51FA\u76EE\u5F55\uFF0C\u5DF2\u62D2\u7EDD\u7EE7\u7EED\u3002");
|
|
21265
22299
|
}
|
|
21266
22300
|
return { outputDir, appRoot, prefix: extracted.prefix };
|
|
@@ -21275,8 +22309,8 @@ var CapabilityJobRunner = class {
|
|
|
21275
22309
|
if (typeof output !== "string" || typeof prefix !== "string") continue;
|
|
21276
22310
|
try {
|
|
21277
22311
|
const outputDir = this.workspace.resolveStoredPath(output);
|
|
21278
|
-
const appRoot =
|
|
21279
|
-
if (
|
|
22312
|
+
const appRoot = import_node_path6.default.resolve(outputDir, prefix);
|
|
22313
|
+
if (import_node_fs7.default.existsSync(appRoot) && (appRoot === outputDir || appRoot.startsWith(outputDir + import_node_path6.default.sep))) {
|
|
21280
22314
|
return { outputDir, appRoot, prefix };
|
|
21281
22315
|
}
|
|
21282
22316
|
} catch {
|
|
@@ -21285,11 +22319,11 @@ var CapabilityJobRunner = class {
|
|
|
21285
22319
|
return void 0;
|
|
21286
22320
|
}
|
|
21287
22321
|
async runReaJson(job, target, folder, args, timeoutMs) {
|
|
21288
|
-
const dir =
|
|
21289
|
-
const rawPath =
|
|
21290
|
-
const stderrPath =
|
|
21291
|
-
const commandPath =
|
|
21292
|
-
|
|
22322
|
+
const dir = import_node_path6.default.join(this.workspace.analysisDir(target), folder, job.id);
|
|
22323
|
+
const rawPath = import_node_path6.default.join(dir, "result.json");
|
|
22324
|
+
const stderrPath = import_node_path6.default.join(dir, "stderr.log");
|
|
22325
|
+
const commandPath = import_node_path6.default.join(dir, "command.json");
|
|
22326
|
+
import_node_fs7.default.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
21293
22327
|
this.workspace.writeJson(commandPath, {
|
|
21294
22328
|
command: this.config.reaPath,
|
|
21295
22329
|
args,
|
|
@@ -21308,7 +22342,7 @@ var CapabilityJobRunner = class {
|
|
|
21308
22342
|
});
|
|
21309
22343
|
this.workspace.writeText(rawPath, result.stdout);
|
|
21310
22344
|
this.workspace.writeText(stderrPath, result.stderr);
|
|
21311
|
-
this.workspace.writeJson(
|
|
22345
|
+
this.workspace.writeJson(import_node_path6.default.join(dir, "status.json"), {
|
|
21312
22346
|
code: result.code,
|
|
21313
22347
|
timedOut: result.timedOut,
|
|
21314
22348
|
finishedAt: now()
|
|
@@ -21322,7 +22356,7 @@ var CapabilityJobRunner = class {
|
|
|
21322
22356
|
if (result.code !== 0) {
|
|
21323
22357
|
const parsed2 = parseJsonDocument(result.stdout);
|
|
21324
22358
|
this.log(job, "warn", "\u5206\u6790\u5F15\u64CE\u672A\u6210\u529F\u5B8C\u6210\uFF0C\u539F\u59CB\u8F93\u51FA\u5DF2\u4FDD\u7559\u3002", {
|
|
21325
|
-
evidencePath:
|
|
22359
|
+
evidencePath: import_node_path6.default.relative(this.workspace.root, rawPath)
|
|
21326
22360
|
});
|
|
21327
22361
|
const message = reaErrorMessage(parsed2) ?? shortFailure(result.stderr, result.stdout);
|
|
21328
22362
|
fail("ENGINE_FAILED", message);
|
|
@@ -21336,7 +22370,7 @@ var CapabilityJobRunner = class {
|
|
|
21336
22370
|
}
|
|
21337
22371
|
return {
|
|
21338
22372
|
parsed,
|
|
21339
|
-
rawPath:
|
|
22373
|
+
rawPath: import_node_path6.default.relative(this.workspace.root, rawPath),
|
|
21340
22374
|
sha256: hashText(result.stdout),
|
|
21341
22375
|
summary: summarizeRea(parsed)
|
|
21342
22376
|
};
|
|
@@ -21384,10 +22418,10 @@ var CapabilityJobRunner = class {
|
|
|
21384
22418
|
}
|
|
21385
22419
|
};
|
|
21386
22420
|
function findAppPrefix(outputDir) {
|
|
21387
|
-
const payload =
|
|
21388
|
-
if (!
|
|
22421
|
+
const payload = import_node_path6.default.join(outputDir, "Payload");
|
|
22422
|
+
if (!import_node_fs7.default.existsSync(payload)) return void 0;
|
|
21389
22423
|
try {
|
|
21390
|
-
const app =
|
|
22424
|
+
const app = import_node_fs7.default.readdirSync(payload, { withFileTypes: true }).find((entry) => entry.isDirectory() && entry.name.endsWith(".app"));
|
|
21391
22425
|
return app ? `Payload/${app.name}/` : void 0;
|
|
21392
22426
|
} catch {
|
|
21393
22427
|
return void 0;
|
|
@@ -21395,11 +22429,12 @@ function findAppPrefix(outputDir) {
|
|
|
21395
22429
|
}
|
|
21396
22430
|
|
|
21397
22431
|
// src/license.ts
|
|
21398
|
-
var
|
|
22432
|
+
var import_node_fs8 = __toESM(require("fs"));
|
|
21399
22433
|
var import_node_crypto2 = __toESM(require("crypto"));
|
|
21400
22434
|
var FREE_FEATURES = /* @__PURE__ */ new Set([
|
|
21401
22435
|
"inspect:ipa",
|
|
21402
22436
|
"inspect:mac-app",
|
|
22437
|
+
"inspect:pe",
|
|
21403
22438
|
"extract:app",
|
|
21404
22439
|
"javascript:analyze",
|
|
21405
22440
|
"generate:report"
|
|
@@ -21415,7 +22450,7 @@ function verifyLicense(token) {
|
|
|
21415
22450
|
if (!encodedPayload || !encodedSignature) {
|
|
21416
22451
|
throw new Error("Invalid license token.");
|
|
21417
22452
|
}
|
|
21418
|
-
const publicKeyValue = process.env.APPTRACE_LICENSE_PUBLIC_KEY || (process.env.APPTRACE_LICENSE_PUBLIC_KEY_FILE ?
|
|
22453
|
+
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);
|
|
21419
22454
|
if (!publicKeyValue) {
|
|
21420
22455
|
throw new Error("APPTRACE_LICENSE_PUBLIC_KEY is not configured.");
|
|
21421
22456
|
}
|
|
@@ -21472,8 +22507,8 @@ function compareVersionData(input2) {
|
|
|
21472
22507
|
}
|
|
21473
22508
|
|
|
21474
22509
|
// src/store.ts
|
|
21475
|
-
var
|
|
21476
|
-
var
|
|
22510
|
+
var import_node_fs9 = __toESM(require("fs"));
|
|
22511
|
+
var import_node_path7 = __toESM(require("path"));
|
|
21477
22512
|
var import_node_module = require("module");
|
|
21478
22513
|
var import_meta = {};
|
|
21479
22514
|
function openSqlite(databasePath) {
|
|
@@ -21726,7 +22761,7 @@ var SqliteStore = class {
|
|
|
21726
22761
|
kind = "sqlite";
|
|
21727
22762
|
db;
|
|
21728
22763
|
constructor(databasePath) {
|
|
21729
|
-
|
|
22764
|
+
import_node_fs9.default.mkdirSync(import_node_path7.default.dirname(databasePath), { recursive: true, mode: 448 });
|
|
21730
22765
|
const db = openSqlite(databasePath);
|
|
21731
22766
|
if (!db) throw new Error("node:sqlite is not available");
|
|
21732
22767
|
this.db = db;
|
|
@@ -22059,11 +23094,11 @@ var JsonStore = class {
|
|
|
22059
23094
|
data;
|
|
22060
23095
|
constructor(databasePath) {
|
|
22061
23096
|
this.filePath = databasePath.replace(/\.sqlite$/i, "") + ".json";
|
|
22062
|
-
|
|
23097
|
+
import_node_fs9.default.mkdirSync(import_node_path7.default.dirname(this.filePath), { recursive: true, mode: 448 });
|
|
22063
23098
|
this.data = this.load();
|
|
22064
23099
|
}
|
|
22065
23100
|
load() {
|
|
22066
|
-
if (!
|
|
23101
|
+
if (!import_node_fs9.default.existsSync(this.filePath)) {
|
|
22067
23102
|
return {
|
|
22068
23103
|
targets: [],
|
|
22069
23104
|
artifacts: [],
|
|
@@ -22075,7 +23110,7 @@ var JsonStore = class {
|
|
|
22075
23110
|
};
|
|
22076
23111
|
}
|
|
22077
23112
|
try {
|
|
22078
|
-
const parsed = JSON.parse(
|
|
23113
|
+
const parsed = JSON.parse(import_node_fs9.default.readFileSync(this.filePath, "utf8"));
|
|
22079
23114
|
return {
|
|
22080
23115
|
targets: parsed.targets ?? [],
|
|
22081
23116
|
artifacts: parsed.artifacts ?? [],
|
|
@@ -22099,8 +23134,8 @@ var JsonStore = class {
|
|
|
22099
23134
|
}
|
|
22100
23135
|
save() {
|
|
22101
23136
|
const temp = `${this.filePath}.${process.pid}.tmp`;
|
|
22102
|
-
|
|
22103
|
-
|
|
23137
|
+
import_node_fs9.default.writeFileSync(temp, JSON.stringify(this.data, null, 2), { mode: 384 });
|
|
23138
|
+
import_node_fs9.default.renameSync(temp, this.filePath);
|
|
22104
23139
|
}
|
|
22105
23140
|
listTargets() {
|
|
22106
23141
|
return [...this.data.targets].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
@@ -22241,44 +23276,44 @@ function createStore(databasePath) {
|
|
|
22241
23276
|
}
|
|
22242
23277
|
|
|
22243
23278
|
// src/workspace.ts
|
|
22244
|
-
var
|
|
22245
|
-
var
|
|
23279
|
+
var import_node_fs10 = __toESM(require("fs"));
|
|
23280
|
+
var import_node_path8 = __toESM(require("path"));
|
|
22246
23281
|
var WorkspaceService = class {
|
|
22247
23282
|
root;
|
|
22248
23283
|
constructor(root) {
|
|
22249
|
-
this.root =
|
|
22250
|
-
|
|
22251
|
-
|
|
22252
|
-
|
|
23284
|
+
this.root = import_node_path8.default.resolve(root);
|
|
23285
|
+
import_node_fs10.default.mkdirSync(this.root, { recursive: true, mode: 448 });
|
|
23286
|
+
import_node_fs10.default.mkdirSync(import_node_path8.default.join(this.root, "targets"), { recursive: true, mode: 448 });
|
|
23287
|
+
import_node_fs10.default.mkdirSync(import_node_path8.default.join(this.root, "tmp"), { recursive: true, mode: 448 });
|
|
22253
23288
|
}
|
|
22254
23289
|
targetDir(target) {
|
|
22255
|
-
return
|
|
23290
|
+
return import_node_path8.default.join(
|
|
22256
23291
|
this.root,
|
|
22257
23292
|
"targets",
|
|
22258
23293
|
`${target.targetRef.platform}-${target.targetRef.sourceId}-${target.slug}-${target.id.slice(0, 8)}`
|
|
22259
23294
|
);
|
|
22260
23295
|
}
|
|
22261
23296
|
artifactDir(target) {
|
|
22262
|
-
return
|
|
23297
|
+
return import_node_path8.default.join(this.targetDir(target), "artifacts");
|
|
22263
23298
|
}
|
|
22264
23299
|
analysisDir(target) {
|
|
22265
|
-
return
|
|
23300
|
+
return import_node_path8.default.join(this.targetDir(target), "analysis");
|
|
22266
23301
|
}
|
|
22267
23302
|
ensureTarget(target) {
|
|
22268
23303
|
const dir = this.targetDir(target);
|
|
22269
23304
|
for (const subdir of ["", "artifacts", "analysis", "reports"]) {
|
|
22270
|
-
|
|
23305
|
+
import_node_fs10.default.mkdirSync(import_node_path8.default.join(dir, subdir), { recursive: true, mode: 448 });
|
|
22271
23306
|
}
|
|
22272
23307
|
}
|
|
22273
23308
|
saveResolvedTarget(target, rawSource) {
|
|
22274
23309
|
this.ensureTarget(target);
|
|
22275
23310
|
const dir = this.targetDir(target);
|
|
22276
|
-
this.writeJson(
|
|
22277
|
-
this.writeJson(
|
|
23311
|
+
this.writeJson(import_node_path8.default.join(dir, "meta.json"), target);
|
|
23312
|
+
this.writeJson(import_node_path8.default.join(dir, "source.json"), rawSource);
|
|
22278
23313
|
}
|
|
22279
23314
|
saveArtifactMetadata(target, artifact, extra = {}) {
|
|
22280
23315
|
const dir = this.targetDir(target);
|
|
22281
|
-
this.writeJson(
|
|
23316
|
+
this.writeJson(import_node_path8.default.join(dir, "artifact.json"), {
|
|
22282
23317
|
schemaVersion: 1,
|
|
22283
23318
|
target: {
|
|
22284
23319
|
id: target.id,
|
|
@@ -22302,7 +23337,7 @@ var WorkspaceService = class {
|
|
|
22302
23337
|
},
|
|
22303
23338
|
...extra
|
|
22304
23339
|
});
|
|
22305
|
-
this.writeJson(
|
|
23340
|
+
this.writeJson(import_node_path8.default.join(dir, "current.json"), {
|
|
22306
23341
|
artifactId: artifact.id,
|
|
22307
23342
|
path: artifact.path,
|
|
22308
23343
|
sha256: artifact.sha256,
|
|
@@ -22310,11 +23345,11 @@ var WorkspaceService = class {
|
|
|
22310
23345
|
});
|
|
22311
23346
|
}
|
|
22312
23347
|
artifactRelativePath(target, filePath) {
|
|
22313
|
-
return
|
|
23348
|
+
return import_node_path8.default.relative(this.root, filePath);
|
|
22314
23349
|
}
|
|
22315
23350
|
resolveStoredPath(relativePath) {
|
|
22316
|
-
const resolved =
|
|
22317
|
-
if (resolved !== this.root && !resolved.startsWith(this.root +
|
|
23351
|
+
const resolved = import_node_path8.default.resolve(this.root, relativePath);
|
|
23352
|
+
if (resolved !== this.root && !resolved.startsWith(this.root + import_node_path8.default.sep)) {
|
|
22318
23353
|
throw new Error(`Path escapes workspace: ${relativePath}`);
|
|
22319
23354
|
}
|
|
22320
23355
|
return resolved;
|
|
@@ -22324,10 +23359,10 @@ var WorkspaceService = class {
|
|
|
22324
23359
|
`);
|
|
22325
23360
|
}
|
|
22326
23361
|
writeText(filePath, value) {
|
|
22327
|
-
|
|
23362
|
+
import_node_fs10.default.mkdirSync(import_node_path8.default.dirname(filePath), { recursive: true, mode: 448 });
|
|
22328
23363
|
const temp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
22329
|
-
|
|
22330
|
-
|
|
23364
|
+
import_node_fs10.default.writeFileSync(temp, value, { mode: 384 });
|
|
23365
|
+
import_node_fs10.default.renameSync(temp, filePath);
|
|
22331
23366
|
}
|
|
22332
23367
|
};
|
|
22333
23368
|
|
|
@@ -22366,6 +23401,8 @@ async function handle(line) {
|
|
|
22366
23401
|
ok: true,
|
|
22367
23402
|
result: { protocolVersion: PROTOCOL_VERSION, capabilities: [
|
|
22368
23403
|
"inspect:ipa",
|
|
23404
|
+
"inspect:mac-app",
|
|
23405
|
+
"inspect:pe",
|
|
22369
23406
|
"extract:app",
|
|
22370
23407
|
"javascript:analyze",
|
|
22371
23408
|
"native:inspect",
|
|
@@ -22393,12 +23430,12 @@ async function handle(line) {
|
|
|
22393
23430
|
});
|
|
22394
23431
|
return;
|
|
22395
23432
|
}
|
|
22396
|
-
const workspaceRoot =
|
|
22397
|
-
if (!
|
|
23433
|
+
const workspaceRoot = import_node_path9.default.resolve(request.workspacePath);
|
|
23434
|
+
if (!import_node_fs11.default.existsSync(workspaceRoot)) {
|
|
22398
23435
|
send({ id: request.id, ok: false, error: { code: "WORKSPACE_NOT_FOUND", message: workspaceRoot } });
|
|
22399
23436
|
return;
|
|
22400
23437
|
}
|
|
22401
|
-
const store = createStore(
|
|
23438
|
+
const store = createStore(import_node_path9.default.join(workspaceRoot, "workbench.sqlite"));
|
|
22402
23439
|
try {
|
|
22403
23440
|
if (request.capability === "compare:versions") {
|
|
22404
23441
|
send({
|