@zhuixin/engine 0.2.3 → 0.2.5

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