@zhuixin/engine 0.2.2 → 0.2.4

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