@trops/dash-core 0.1.485 → 0.1.487

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.
@@ -4,7 +4,7 @@ var require$$0$1 = require('electron');
4
4
  var require$$1$1 = require('electron-store');
5
5
  var require$$1$2 = require('path');
6
6
  var require$$0$2 = require('fs');
7
- var require$$5$1 = require('objects-to-csv');
7
+ var require$$6$1 = require('objects-to-csv');
8
8
  var require$$1$3 = require('readline');
9
9
  var require$$2 = require('xtreamer');
10
10
  var require$$3$1 = require('xml2js');
@@ -12,12 +12,12 @@ var require$$4 = require('JSONStream');
12
12
  var require$$5 = require('stream');
13
13
  var require$$6 = require('csv-parser');
14
14
  var require$$0$3 = require('quickjs-emscripten');
15
- var require$$7 = require('https');
15
+ var require$$8$1 = require('https');
16
16
  var require$$0$5 = require('@modelcontextprotocol/sdk/client/index.js');
17
17
  var require$$1$4 = require('@modelcontextprotocol/sdk/client/stdio.js');
18
18
  var require$$0$4 = require('pkce-challenge');
19
19
  var require$$2$1 = require('os');
20
- var require$$7$1 = require('child_process');
20
+ var require$$7 = require('child_process');
21
21
  var require$$3$2 = require('adm-zip');
22
22
  var require$$4$1 = require('url');
23
23
  var require$$2$2 = require('vm');
@@ -28,7 +28,7 @@ var require$$0$6 = require('openai');
28
28
  require('live-plugin-manager');
29
29
  var require$$0$9 = require('@anthropic-ai/sdk');
30
30
  var require$$3$4 = require('crypto');
31
- var require$$8$1 = require('zod');
31
+ var require$$8$2 = require('zod');
32
32
  var require$$0$7 = require('http');
33
33
  var require$$1$6 = require('http2');
34
34
  var require$$2$4 = require('node-forge');
@@ -1107,7 +1107,7 @@ var secureStoreController$1 = {
1107
1107
  getData: getData$1,
1108
1108
  };
1109
1109
 
1110
- const path$j = require$$1$2;
1110
+ const path$k = require$$1$2;
1111
1111
  const {
1112
1112
  readFileSync,
1113
1113
  writeFileSync: writeFileSync$4,
@@ -1125,7 +1125,7 @@ const {
1125
1125
  function ensureDirectoryExistence$2(filePath) {
1126
1126
  try {
1127
1127
  // isDirectory
1128
- var dirname = path$j.dirname(filePath);
1128
+ var dirname = path$k.dirname(filePath);
1129
1129
  // check if the directory exists...return true
1130
1130
  // if not, we can pass in the dirname as the filepath
1131
1131
  // and check each directory recursively.
@@ -1240,7 +1240,7 @@ function removeFilesFromDirectory(directory, excludeFiles = []) {
1240
1240
 
1241
1241
  for (const file of files) {
1242
1242
  if (!excludeFiles.includes(file)) {
1243
- unlinkSync(path$j.join(directory, file), (err) => {
1243
+ unlinkSync(path$k.join(directory, file), (err) => {
1244
1244
  if (err) throw err;
1245
1245
  });
1246
1246
  }
@@ -1257,8 +1257,8 @@ var file = {
1257
1257
  checkDirectory: checkDirectory$1,
1258
1258
  };
1259
1259
 
1260
- const { app: app$b } = require$$0$1;
1261
- const path$i = require$$1$2;
1260
+ const { app: app$c } = require$$0$1;
1261
+ const path$j = require$$1$2;
1262
1262
  const { writeFileSync: writeFileSync$3 } = require$$0$2;
1263
1263
  const { getFileContents: getFileContents$7 } = file;
1264
1264
 
@@ -1305,8 +1305,8 @@ const workspaceController$3 = {
1305
1305
  saveWorkspaceForApplication: (win, appId, workspaceObject) => {
1306
1306
  try {
1307
1307
  // filename to the pages file (live pages)
1308
- const filename = path$i.join(
1309
- app$b.getPath("userData"),
1308
+ const filename = path$j.join(
1309
+ app$c.getPath("userData"),
1310
1310
  appName$7,
1311
1311
  appId,
1312
1312
  configFilename$5,
@@ -1354,8 +1354,8 @@ const workspaceController$3 = {
1354
1354
  saveMenuItemsForApplication: (win, appId, menuItems) => {
1355
1355
  try {
1356
1356
  // filename to the workspaces file
1357
- const filename = path$i.join(
1358
- app$b.getPath("userData"),
1357
+ const filename = path$j.join(
1358
+ app$c.getPath("userData"),
1359
1359
  appName$7,
1360
1360
  appId,
1361
1361
  configFilename$5,
@@ -1403,8 +1403,8 @@ const workspaceController$3 = {
1403
1403
  */
1404
1404
  deleteWorkspaceForApplication: (win, appId, workspaceId) => {
1405
1405
  try {
1406
- const filename = path$i.join(
1407
- app$b.getPath("userData"),
1406
+ const filename = path$j.join(
1407
+ app$c.getPath("userData"),
1408
1408
  appName$7,
1409
1409
  appId,
1410
1410
  configFilename$5,
@@ -1437,8 +1437,8 @@ const workspaceController$3 = {
1437
1437
 
1438
1438
  listWorkspacesForApplication: (win, appId) => {
1439
1439
  try {
1440
- const filename = path$i.join(
1441
- app$b.getPath("userData"),
1440
+ const filename = path$j.join(
1441
+ app$c.getPath("userData"),
1442
1442
  appName$7,
1443
1443
  appId,
1444
1444
  configFilename$5,
@@ -1465,8 +1465,8 @@ const workspaceController$3 = {
1465
1465
 
1466
1466
  listMenuItemsForApplication: (win, appId) => {
1467
1467
  try {
1468
- const filename = path$i.join(
1469
- app$b.getPath("userData"),
1468
+ const filename = path$j.join(
1469
+ app$c.getPath("userData"),
1470
1470
  appName$7,
1471
1471
  appId,
1472
1472
  configFilename$5,
@@ -1509,8 +1509,8 @@ const workspaceController$3 = {
1509
1509
 
1510
1510
  var workspaceController_1 = workspaceController$3;
1511
1511
 
1512
- const { app: app$a } = require$$0$1;
1513
- const path$h = require$$1$2;
1512
+ const { app: app$b } = require$$0$1;
1513
+ const path$i = require$$1$2;
1514
1514
  const { writeFileSync: writeFileSync$2 } = require$$0$2;
1515
1515
  const { getFileContents: getFileContents$6 } = file;
1516
1516
 
@@ -1530,8 +1530,8 @@ const themeController$5 = {
1530
1530
  saveThemeForApplication: (win, appId, name, obj) => {
1531
1531
  try {
1532
1532
  // filename to the pages file (live pages)
1533
- const filename = path$h.join(
1534
- app$a.getPath("userData"),
1533
+ const filename = path$i.join(
1534
+ app$b.getPath("userData"),
1535
1535
  appName$6,
1536
1536
  appId,
1537
1537
  configFilename$4,
@@ -1576,8 +1576,8 @@ const themeController$5 = {
1576
1576
  */
1577
1577
  listThemesForApplication: (win, appId) => {
1578
1578
  try {
1579
- const filename = path$h.join(
1580
- app$a.getPath("userData"),
1579
+ const filename = path$i.join(
1580
+ app$b.getPath("userData"),
1581
1581
  appName$6,
1582
1582
  appId,
1583
1583
  configFilename$4,
@@ -1618,8 +1618,8 @@ const themeController$5 = {
1618
1618
  */
1619
1619
  deleteThemeForApplication: (win, appId, themeKey) => {
1620
1620
  try {
1621
- const filename = path$h.join(
1622
- app$a.getPath("userData"),
1621
+ const filename = path$i.join(
1622
+ app$b.getPath("userData"),
1623
1623
  appName$6,
1624
1624
  appId,
1625
1625
  configFilename$4,
@@ -1650,6 +1650,179 @@ const themeController$5 = {
1650
1650
 
1651
1651
  var themeController_1 = themeController$5;
1652
1652
 
1653
+ /**
1654
+ * safePath.js
1655
+ *
1656
+ * Path-traversal containment for IPC handlers that accept renderer-
1657
+ * supplied paths.
1658
+ *
1659
+ * Why: dash-core exposes IPC handlers (mainApi.data.saveData,
1660
+ * mainApi.data.parseXMLStream, mainApi.algolia.createBatchesFromFile,
1661
+ * etc.) that historically passed renderer-controlled paths directly to
1662
+ * fs.writeFileSync / fs.createReadStream. A widget could pass
1663
+ * "../../etc/passwd" and the handler would write/read OUTSIDE the
1664
+ * intended app data directory because path.join doesn't reject `..`
1665
+ * segments. See docs/security/ipc-filesystem-audit.md for the full
1666
+ * finding set.
1667
+ *
1668
+ * This utility resolves the requested path, walks symlinks, and asserts
1669
+ * containment within at least one explicitly-allowed root. Any handler
1670
+ * that takes a renderer path runs it through `safePath(p, roots)` and
1671
+ * either gets back a validated absolute real-path, or throws.
1672
+ *
1673
+ * Public API:
1674
+ *
1675
+ * safePath(requested, allowedRoots[]) → string
1676
+ * Throws on traversal, missing input, or empty roots. Returns the
1677
+ * resolved real-path (which is what the caller should pass to fs).
1678
+ *
1679
+ * getAllowedRoots(category) → string[]
1680
+ * Canonical roots per category. Categories:
1681
+ * "data" — userData/Dashboard/data + user-configured override
1682
+ * "themes" — userData/Dashboard/themes
1683
+ * "widgets" — userData/widgets
1684
+ * "plugins" — userData/plugins
1685
+ * "downloads"— OS Downloads folder
1686
+ *
1687
+ * Defense layers:
1688
+ * 1. path.resolve() to absolute form.
1689
+ * 2. fs.realpathSync() through any symlinks. If the path doesn't
1690
+ * exist yet, realpath the parent directory (so a symlink-in-parent
1691
+ * can't trick a future create operation).
1692
+ * 3. startsWith(realRoot + path.sep) test — single-equals check
1693
+ * handles "exactly the root" case, prefix-with-sep handles
1694
+ * "inside the root" without false-matching `/data-evil/` against
1695
+ * `/data/`.
1696
+ */
1697
+
1698
+ const path$h = require$$1$2;
1699
+ const fs$c = require$$0$2;
1700
+ const { app: app$a } = require$$0$1;
1701
+
1702
+ const APP_NAME = "Dashboard";
1703
+
1704
+ /**
1705
+ * @param {string} category
1706
+ * @returns {string[]} ordered allowed roots for that category
1707
+ */
1708
+ function getAllowedRoots$2(category) {
1709
+ const userData = app$a.getPath("userData");
1710
+ switch (category) {
1711
+ case "data": {
1712
+ const def = path$h.join(userData, APP_NAME, "data");
1713
+ // The user can configure a custom data directory in
1714
+ // Settings → General → Data Directory. If set, that
1715
+ // location is ALSO an allowed root. We don't replace the
1716
+ // default — both are valid because legacy data may still
1717
+ // live in the default while new data goes to the override.
1718
+ const override = readDataDirectoryFromSettings();
1719
+ return override ? [def, override] : [def];
1720
+ }
1721
+ case "themes":
1722
+ return [path$h.join(userData, APP_NAME, "themes")];
1723
+ case "widgets":
1724
+ return [path$h.join(userData, "widgets")];
1725
+ case "plugins":
1726
+ return [path$h.join(userData, "plugins")];
1727
+ case "downloads":
1728
+ return [app$a.getPath("downloads")];
1729
+ default:
1730
+ throw new Error("safePath: unknown allowed-roots category: " + category);
1731
+ }
1732
+ }
1733
+
1734
+ /**
1735
+ * Read the user-configured data directory from settings.json. Returns
1736
+ * undefined if not set or unreadable.
1737
+ *
1738
+ * Inlined to avoid a circular require with settingsController. Reads
1739
+ * the same settings.json file directly.
1740
+ */
1741
+ function readDataDirectoryFromSettings() {
1742
+ try {
1743
+ const settingsPath = path$h.join(
1744
+ app$a.getPath("userData"),
1745
+ APP_NAME,
1746
+ "settings.json",
1747
+ );
1748
+ if (!fs$c.existsSync(settingsPath)) return undefined;
1749
+ const raw = fs$c.readFileSync(settingsPath, "utf8");
1750
+ const settings = JSON.parse(raw);
1751
+ const dir = settings && settings.dataDirectory;
1752
+ if (typeof dir === "string" && dir) return dir;
1753
+ } catch (_e) {
1754
+ // best-effort — fall through to default
1755
+ }
1756
+ return undefined;
1757
+ }
1758
+
1759
+ /**
1760
+ * Resolve and validate a path against allowed roots.
1761
+ *
1762
+ * @param {string} requested the path the renderer asked for
1763
+ * @param {string[]} allowedRoots list of absolute paths the result must be inside
1764
+ * @returns {string} validated absolute real-path
1765
+ * @throws if requested is not contained within any allowed root
1766
+ */
1767
+ function safePath$2(requested, allowedRoots) {
1768
+ if (typeof requested !== "string" || !requested) {
1769
+ throw new Error("safePath: requested must be a non-empty string");
1770
+ }
1771
+ if (!Array.isArray(allowedRoots) || allowedRoots.length === 0) {
1772
+ throw new Error("safePath: allowedRoots must be a non-empty array");
1773
+ }
1774
+
1775
+ const resolved = path$h.resolve(requested);
1776
+
1777
+ // Real-path through symlinks. If the file doesn't exist yet (a
1778
+ // create-new operation), real-path the parent so a symlink in the
1779
+ // parent chain can't trick us.
1780
+ let real = resolved;
1781
+ try {
1782
+ real = fs$c.realpathSync(resolved);
1783
+ } catch (_e) {
1784
+ try {
1785
+ const parent = fs$c.realpathSync(path$h.dirname(resolved));
1786
+ real = path$h.join(parent, path$h.basename(resolved));
1787
+ } catch (_e2) {
1788
+ // Parent doesn't exist either. Use the resolved-but-not-
1789
+ // real path; the caller's mkdirSync will happen inside the
1790
+ // validated root, and any symlinks underneath will be
1791
+ // re-checked the next time safePath sees the same path.
1792
+ }
1793
+ }
1794
+
1795
+ for (const root of allowedRoots) {
1796
+ let realRoot = root;
1797
+ try {
1798
+ if (fs$c.existsSync(root)) realRoot = fs$c.realpathSync(root);
1799
+ } catch (_e) {
1800
+ // root doesn't exist or isn't reachable — keep as-is for
1801
+ // the comparison below
1802
+ }
1803
+ // Exact match OR strictly-inside (with separator to prevent
1804
+ // /data-evil/ matching /data/).
1805
+ if (real === realRoot || real.startsWith(realRoot + path$h.sep)) {
1806
+ return real;
1807
+ }
1808
+ }
1809
+
1810
+ throw new Error(
1811
+ "safePath: requested path is not within any allowed root. " +
1812
+ "Requested: " +
1813
+ requested +
1814
+ " (resolved to " +
1815
+ real +
1816
+ "). Allowed roots: " +
1817
+ allowedRoots.join(", "),
1818
+ );
1819
+ }
1820
+
1821
+ var safePath_1 = {
1822
+ safePath: safePath$2,
1823
+ getAllowedRoots: getAllowedRoots$2,
1824
+ };
1825
+
1653
1826
  /**
1654
1827
  * safeJsExecutor.js
1655
1828
  *
@@ -2337,11 +2510,12 @@ var fs$a = require$$0$2;
2337
2510
  const path$f = require$$1$2;
2338
2511
  const events$5 = events$8;
2339
2512
  const { getFileContents: getFileContents$5, writeToFile: writeToFile$2 } = file;
2513
+ const { safePath: safePath$1, getAllowedRoots: getAllowedRoots$1 } = safePath_1;
2340
2514
 
2341
2515
  // Convert Json to Csv
2342
- const ObjectsToCsv = require$$5$1;
2516
+ const ObjectsToCsv = require$$6$1;
2343
2517
  const Transform = transform;
2344
- const https$3 = require$$7;
2518
+ const https$3 = require$$8$1;
2345
2519
  const appName$5 = "Dashboard";
2346
2520
 
2347
2521
  const dataController$1 = {
@@ -2355,14 +2529,25 @@ const dataController$1 = {
2355
2529
  */
2356
2530
  convertJsonToCsvFile: (win, appId, jsonObject, toFilename = "test.csv") => {
2357
2531
  try {
2358
- // filename to the pages file (live pages)
2359
- const filename = path$f.join(
2532
+ // Validate the renderer-supplied filename is contained within
2533
+ // the data directory. path.join doesn't reject `..` segments;
2534
+ // safePath does.
2535
+ const candidate = path$f.join(
2360
2536
  app$8.getPath("userData"),
2361
2537
  appName$5,
2362
2538
  appId,
2363
2539
  "data",
2364
2540
  toFilename,
2365
2541
  );
2542
+ let filename;
2543
+ try {
2544
+ filename = safePath$1(candidate, getAllowedRoots$1("data"));
2545
+ } catch (pathErr) {
2546
+ win.webContents.send(events$5.DATA_JSON_TO_CSV_FILE_ERROR, {
2547
+ error: pathErr.message,
2548
+ });
2549
+ return;
2550
+ }
2366
2551
 
2367
2552
  // make sure the file exists...
2368
2553
  const fileContents = getFileContents$5(filename, "");
@@ -2420,8 +2605,17 @@ const dataController$1 = {
2420
2605
 
2421
2606
  readLinesFromFile: (win, filepath, lineCount) => {
2422
2607
  try {
2608
+ let validated;
2609
+ try {
2610
+ validated = safePath$1(filepath, getAllowedRoots$1("data"));
2611
+ } catch (pathErr) {
2612
+ win.webContents.send(events$5.READ_LINES_ERROR, {
2613
+ error: pathErr.message,
2614
+ });
2615
+ return;
2616
+ }
2423
2617
  const t = new Transform();
2424
- t.readLinesFromFile(win, filepath, lineCount, events$5.READ_LINES_UPDATE)
2618
+ t.readLinesFromFile(win, validated, lineCount, events$5.READ_LINES_UPDATE)
2425
2619
  .then((res) => {
2426
2620
  win.webContents.send(events$5.READ_LINES_COMPLETE, {
2427
2621
  success: true,
@@ -2445,9 +2639,18 @@ const dataController$1 = {
2445
2639
 
2446
2640
  readJSONFromFile: (win, filepath, objectCount = null) => {
2447
2641
  try {
2448
- console.log("reading json from file ", filepath, objectCount);
2642
+ let validated;
2643
+ try {
2644
+ validated = safePath$1(filepath, getAllowedRoots$1("data"));
2645
+ } catch (pathErr) {
2646
+ win.webContents.send(events$5.READ_JSON_ERROR, {
2647
+ error: pathErr.message,
2648
+ });
2649
+ return;
2650
+ }
2651
+ console.log("reading json from file ", validated, objectCount);
2449
2652
  const t = new Transform();
2450
- t.readJSONFromFile(win, filepath, objectCount, events$5.READ_JSON_UPDATE)
2653
+ t.readJSONFromFile(win, validated, objectCount, events$5.READ_JSON_UPDATE)
2451
2654
  .then((res) => {
2452
2655
  win.webContents.send(events$5.READ_JSON_COMPLETE, {
2453
2656
  success: true,
@@ -2483,14 +2686,10 @@ const dataController$1 = {
2483
2686
  );
2484
2687
  }
2485
2688
 
2486
- // Validate toFilepath is within the app data directory
2487
- const appDataDir = path$f.join(app$8.getPath("userData"), appName$5);
2488
- const resolvedFilepath = path$f.resolve(toFilepath);
2489
- if (!resolvedFilepath.startsWith(appDataDir + path$f.sep)) {
2490
- throw new Error(
2491
- "File path must be within the application data directory",
2492
- );
2493
- }
2689
+ // Validate toFilepath is within the app data directory.
2690
+ // safePath replaces the previous inline check; same containment
2691
+ // intent, plus realpath/symlink protection.
2692
+ const resolvedFilepath = safePath$1(toFilepath, getAllowedRoots$1("data"));
2494
2693
 
2495
2694
  const writeStream = fs$a.createWriteStream(resolvedFilepath);
2496
2695
 
@@ -2537,10 +2736,21 @@ const dataController$1 = {
2537
2736
  objectIdKey = null,
2538
2737
  ) => {
2539
2738
  try {
2739
+ let validatedIn, validatedOut;
2740
+ try {
2741
+ const roots = getAllowedRoots$1("data");
2742
+ validatedIn = safePath$1(filepath, roots);
2743
+ validatedOut = safePath$1(outpath, roots);
2744
+ } catch (pathErr) {
2745
+ win.webContents.send(events$5.PARSE_XML_STREAM_ERROR, {
2746
+ error: pathErr.message,
2747
+ });
2748
+ return;
2749
+ }
2540
2750
  const t = new Transform();
2541
2751
  t.parseXMLStream(
2542
- filepath,
2543
- outpath,
2752
+ validatedIn,
2753
+ validatedOut,
2544
2754
  start,
2545
2755
  // recordNode,
2546
2756
  // objectIdKey,
@@ -2586,10 +2796,21 @@ const dataController$1 = {
2586
2796
  limit = null,
2587
2797
  ) => {
2588
2798
  try {
2799
+ let validatedIn, validatedOut;
2800
+ try {
2801
+ const roots = getAllowedRoots$1("data");
2802
+ validatedIn = safePath$1(filepath, roots);
2803
+ validatedOut = safePath$1(outpath, roots);
2804
+ } catch (pathErr) {
2805
+ win.webContents.send(events$5.PARSE_CSV_STREAM_ERROR, {
2806
+ error: pathErr.message,
2807
+ });
2808
+ return;
2809
+ }
2589
2810
  const t = new Transform();
2590
2811
  t.parseCSVStream(
2591
- filepath,
2592
- outpath,
2812
+ validatedIn,
2813
+ validatedOut,
2593
2814
  delimiter,
2594
2815
  objectIdKey,
2595
2816
  headers,
@@ -2632,13 +2853,25 @@ const dataController$1 = {
2632
2853
  saveToFile: (win, data, filename, append, returnEmpty = {}) => {
2633
2854
  try {
2634
2855
  if (data) {
2635
- // filename to the pages file (live pages)
2636
- const toFilename = path$f.join(
2856
+ // Validate filename is contained within the data directory.
2857
+ // path.join doesn't reject `..` segments; safePath does.
2858
+ const candidate = path$f.join(
2637
2859
  app$8.getPath("userData"),
2638
2860
  appName$5,
2639
2861
  "data",
2640
2862
  filename,
2641
2863
  );
2864
+ let toFilename;
2865
+ try {
2866
+ toFilename = safePath$1(candidate, getAllowedRoots$1("data"));
2867
+ } catch (pathErr) {
2868
+ win.webContents.send(events$5.DATA_SAVE_TO_FILE_ERROR, {
2869
+ success: false,
2870
+ filename,
2871
+ message: pathErr.message,
2872
+ });
2873
+ return;
2874
+ }
2642
2875
 
2643
2876
  //console.log("saving to file ", toFilename);
2644
2877
 
@@ -21078,7 +21311,7 @@ function getShellPath$1() {
21078
21311
  return _shellPath$1;
21079
21312
  }
21080
21313
 
21081
- const { execSync } = require$$7$1;
21314
+ const { execSync } = require$$7;
21082
21315
  const fallbackDirs = ["/usr/local/bin", "/opt/homebrew/bin"];
21083
21316
 
21084
21317
  // Scan nvm versions, tracking both latest and best compatible version
@@ -21258,7 +21491,7 @@ async function refreshGoogleOAuthToken(tokenRefresh) {
21258
21491
 
21259
21492
  console.log("[mcpController] Refreshing Google OAuth token...");
21260
21493
 
21261
- const https = require$$7;
21494
+ const https = require$$8$1;
21262
21495
  const postData = [
21263
21496
  `client_id=${encodeURIComponent(keyData.client_id)}`,
21264
21497
  `client_secret=${encodeURIComponent(keyData.client_secret)}`,
@@ -21951,7 +22184,7 @@ const mcpController$3 = {
21951
22184
  * @returns {{ success } | { error, message }}
21952
22185
  */
21953
22186
  runAuth: async (win, mcpConfig, credentials, authCommand) => {
21954
- const { spawn } = require$$7$1;
22187
+ const { spawn } = require$$7;
21955
22188
 
21956
22189
  const env = cleanEnvForChildProcess();
21957
22190
 
@@ -25973,6 +26206,7 @@ const algoliasearch = require$$2$3;
25973
26206
  const events$3 = events$8;
25974
26207
  const AlgoliaIndex = algolia;
25975
26208
  var fs$3 = require$$0$2;
26209
+ const { safePath, getAllowedRoots } = safePath_1;
25976
26210
 
25977
26211
  const algoliaController$1 = {
25978
26212
  /**
@@ -26129,10 +26363,19 @@ const algoliaController$1 = {
26129
26363
  createIfNotExists = false,
26130
26364
  ) {
26131
26365
  try {
26366
+ let validatedDir;
26367
+ try {
26368
+ validatedDir = safePath(dir, getAllowedRoots("data"));
26369
+ } catch (pathErr) {
26370
+ win.webContents.send(events$3.ALGOLIA_PARTIAL_UPDATE_OBJECTS_ERROR, {
26371
+ error: pathErr.message,
26372
+ });
26373
+ return;
26374
+ }
26132
26375
  const a = new AlgoliaIndex(appId, apiKey, indexName);
26133
26376
  // now we can make the call to the utility and we are passing in the createIfNotExists FALSE by default
26134
26377
  a.partialUpdateObjectsFromDirectorySync(
26135
- dir,
26378
+ validatedDir,
26136
26379
  createIfNotExists,
26137
26380
  (data) => {
26138
26381
  win.webContents.send(
@@ -26172,10 +26415,21 @@ const algoliaController$1 = {
26172
26415
  batchSize = 500,
26173
26416
  ) => {
26174
26417
  try {
26418
+ let validatedIn, validatedOut;
26419
+ try {
26420
+ const roots = getAllowedRoots("data");
26421
+ validatedIn = safePath(filepath, roots);
26422
+ validatedOut = safePath(batchFilepath, roots);
26423
+ } catch (pathErr) {
26424
+ win.webContents.send(events$3.ALGOLIA_CREATE_BATCH_ERROR, {
26425
+ error: pathErr.message,
26426
+ });
26427
+ return;
26428
+ }
26175
26429
  const a = new AlgoliaIndex();
26176
26430
  a.createBatchesFromJSONFile(
26177
- filepath,
26178
- batchFilepath,
26431
+ validatedIn,
26432
+ validatedOut,
26179
26433
  batchSize,
26180
26434
  (data) => {
26181
26435
  win.webContents.send(events$3.ALGOLIA_CREATE_BATCH_UPDATE, data);
@@ -43641,7 +43895,7 @@ const completable_js_1 = completable;
43641
43895
  const uriTemplate_js_1 = uriTemplate;
43642
43896
  const toolNameValidation_js_1 = toolNameValidation;
43643
43897
  const mcp_server_js_1 = mcpServer$1;
43644
- const zod_1 = require$$8$1;
43898
+ const zod_1 = require$$8$2;
43645
43899
  /**
43646
43900
  * High-level MCP server that provides a simpler API for working with resources, tools, and prompts.
43647
43901
  * For advanced usage (like sending notifications or setting custom request handlers), use the underlying
@@ -46274,7 +46528,7 @@ var tlsCert = { getOrCreateCert: getOrCreateCert$1 };
46274
46528
  * for Zod schemas in tool input validation (safeParseAsync).
46275
46529
  */
46276
46530
 
46277
- const z$1 = require$$8$1;
46531
+ const z$1 = require$$8$2;
46278
46532
 
46279
46533
  /**
46280
46534
  * Convert a JSON Schema property definition to a Zod v3 schema.
@@ -46367,7 +46621,7 @@ var jsonSchemaToZod_1 = { jsonSchemaToZod: jsonSchemaToZod$1, jsonSchemaProperty
46367
46621
  * - Rate limiting via token bucket (60 req/min)
46368
46622
  */
46369
46623
 
46370
- const https$1 = require$$7;
46624
+ const https$1 = require$$8$1;
46371
46625
  const { randomUUID } = require$$3$4;
46372
46626
  const { BrowserWindow: BrowserWindow$1 } = require$$0$1;
46373
46627
  const { McpServer } = mcp;
@@ -46498,7 +46752,7 @@ function registerPrompt$1(promptDef) {
46498
46752
  registeredPrompts.push(promptDef);
46499
46753
  }
46500
46754
 
46501
- const z = require$$8$1;
46755
+ const z = require$$8$2;
46502
46756
  const { jsonSchemaToZod } = jsonSchemaToZod_1;
46503
46757
 
46504
46758
  /**
@@ -46879,7 +47133,7 @@ var mcpDashServerController_1 = mcpDashServerController$4;
46879
47133
  * can use the Chat widget without a separate API key.
46880
47134
  */
46881
47135
 
46882
- const { spawn, execSync } = require$$7$1;
47136
+ const { spawn, execSync } = require$$7;
46883
47137
  const {
46884
47138
  LLM_STREAM_DELTA: LLM_STREAM_DELTA$2,
46885
47139
  LLM_STREAM_TOOL_CALL: LLM_STREAM_TOOL_CALL$2,
@@ -48176,7 +48430,7 @@ var themeFromUrlErrors$1 = {
48176
48430
 
48177
48431
  const css = require$$0$8;
48178
48432
  const { Vibrant } = require$$1$7;
48179
- const https = require$$7;
48433
+ const https = require$$8$1;
48180
48434
  const http = require$$0$7;
48181
48435
  const { URL: URL$1 } = require$$4$1;
48182
48436
  const {