@trops/dash-core 0.1.507 → 0.1.509
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/electron/index.js +208 -18
- package/dist/electron/index.js.map +1 -1
- package/package.json +1 -1
package/dist/electron/index.js
CHANGED
|
@@ -993,14 +993,14 @@ var events$8 = {
|
|
|
993
993
|
* Open a dialog window for choosing files
|
|
994
994
|
*/
|
|
995
995
|
|
|
996
|
-
const { dialog: dialog$
|
|
996
|
+
const { dialog: dialog$3 } = require$$0$1;
|
|
997
997
|
const events$7 = events$8;
|
|
998
998
|
|
|
999
999
|
const showDialog$1 = async (win, message, allowFile, extensions = ["*"]) => {
|
|
1000
1000
|
const properties =
|
|
1001
1001
|
allowFile === true ? ["openFile"] : ["openDirectory", "createDirectory"];
|
|
1002
1002
|
const filters = allowFile === true ? [{ name: "Data", extensions }] : [];
|
|
1003
|
-
const result = await dialog$
|
|
1003
|
+
const result = await dialog$3.showOpenDialog({ properties, filters });
|
|
1004
1004
|
if (result.canceled || !result.filePaths[0]) return null;
|
|
1005
1005
|
return result.filePaths[0];
|
|
1006
1006
|
};
|
|
@@ -2083,7 +2083,7 @@ var grantedPermissions = {
|
|
|
2083
2083
|
* _resetForTest() → void (test-only)
|
|
2084
2084
|
*/
|
|
2085
2085
|
|
|
2086
|
-
const { BrowserWindow: BrowserWindow$
|
|
2086
|
+
const { BrowserWindow: BrowserWindow$3, ipcMain: ipcMain$2 } = require$$0$1;
|
|
2087
2087
|
|
|
2088
2088
|
const REQUEST_CHANNEL = "widget:permission-required";
|
|
2089
2089
|
const RESPONSE_CHANNEL = "widget:permission-response";
|
|
@@ -2129,7 +2129,7 @@ function coalesceKeyOf(req) {
|
|
|
2129
2129
|
function emitEvent(payload) {
|
|
2130
2130
|
let wins = [];
|
|
2131
2131
|
try {
|
|
2132
|
-
wins = BrowserWindow$
|
|
2132
|
+
wins = BrowserWindow$3.getAllWindows() || [];
|
|
2133
2133
|
} catch {
|
|
2134
2134
|
wins = [];
|
|
2135
2135
|
}
|
|
@@ -2586,9 +2586,23 @@ function _hostMatches(host, allowedList) {
|
|
|
2586
2586
|
if (!Array.isArray(allowedList) || allowedList.length === 0) return false;
|
|
2587
2587
|
if (allowedList.includes("*")) return true;
|
|
2588
2588
|
const lower = host.toLowerCase();
|
|
2589
|
-
|
|
2590
|
-
(
|
|
2591
|
-
|
|
2589
|
+
for (const entry of allowedList) {
|
|
2590
|
+
if (typeof entry !== "string") continue;
|
|
2591
|
+
const entryLower = entry.toLowerCase();
|
|
2592
|
+
// Exact match.
|
|
2593
|
+
if (entryLower === lower) return true;
|
|
2594
|
+
// Subdomain wildcard: "*.example.com" matches "example.com" and
|
|
2595
|
+
// any host ending in ".example.com". The leading dot on the
|
|
2596
|
+
// suffix-test is required — otherwise "*.example.com" would also
|
|
2597
|
+
// match "attackerexample.com", which is the kind of confusion
|
|
2598
|
+
// this feature is meant to avoid.
|
|
2599
|
+
if (entryLower.startsWith("*.")) {
|
|
2600
|
+
const base = entryLower.slice(2); // "example.com"
|
|
2601
|
+
if (lower === base) return true;
|
|
2602
|
+
if (lower.endsWith("." + base)) return true;
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
return false;
|
|
2592
2606
|
}
|
|
2593
2607
|
|
|
2594
2608
|
function _parseHost(url) {
|
|
@@ -48726,7 +48740,7 @@ var jsonSchemaToZod_1 = { jsonSchemaToZod: jsonSchemaToZod$1, jsonSchemaProperty
|
|
|
48726
48740
|
|
|
48727
48741
|
const https$1 = require$$11;
|
|
48728
48742
|
const { randomUUID } = require$$3$4;
|
|
48729
|
-
const { BrowserWindow: BrowserWindow$
|
|
48743
|
+
const { BrowserWindow: BrowserWindow$2 } = require$$0$1;
|
|
48730
48744
|
const { McpServer } = mcp;
|
|
48731
48745
|
const {
|
|
48732
48746
|
StreamableHTTPServerTransport,
|
|
@@ -48770,7 +48784,7 @@ function broadcastStateChanged(toolName, result) {
|
|
|
48770
48784
|
/* leave null */
|
|
48771
48785
|
}
|
|
48772
48786
|
const payload = { toolName, result: parsed };
|
|
48773
|
-
for (const win of BrowserWindow$
|
|
48787
|
+
for (const win of BrowserWindow$2.getAllWindows()) {
|
|
48774
48788
|
if (!win.isDestroyed()) {
|
|
48775
48789
|
try {
|
|
48776
48790
|
win.webContents.send("dash-mcp:state-changed", payload);
|
|
@@ -57551,7 +57565,7 @@ function requireWidgetPublishManifest () {
|
|
|
57551
57565
|
* and registry interaction.
|
|
57552
57566
|
*/
|
|
57553
57567
|
const path$4 = require$$1$1;
|
|
57554
|
-
const { app: app$4, dialog: dialog$
|
|
57568
|
+
const { app: app$4, dialog: dialog$2 } = require$$0$1;
|
|
57555
57569
|
const AdmZip$2 = require$$3$2;
|
|
57556
57570
|
|
|
57557
57571
|
const themeController$3 = themeController_1;
|
|
@@ -57750,7 +57764,7 @@ async function prepareThemeForPublish$1(win, appId, themeKey, options = {}) {
|
|
|
57750
57764
|
const sanitizedName = sanitizeName(themeKey);
|
|
57751
57765
|
const defaultFilename = `theme-${sanitizedName}-v${manifest.version}.zip`;
|
|
57752
57766
|
|
|
57753
|
-
const saveResult = await dialog$
|
|
57767
|
+
const saveResult = await dialog$2.showSaveDialog(win, {
|
|
57754
57768
|
title: "Save Theme Package",
|
|
57755
57769
|
defaultPath: defaultFilename,
|
|
57756
57770
|
filters: [{ name: "ZIP Files", extensions: ["zip"] }],
|
|
@@ -58289,7 +58303,7 @@ var themeRegistryController$1 = {
|
|
|
58289
58303
|
* applies event wiring. (Import is implemented in DASH-13.)
|
|
58290
58304
|
*/
|
|
58291
58305
|
|
|
58292
|
-
const { app: app$3, dialog } = require$$0$1;
|
|
58306
|
+
const { app: app$3, dialog: dialog$1 } = require$$0$1;
|
|
58293
58307
|
const path$3 = require$$1$1;
|
|
58294
58308
|
const AdmZip$1 = require$$3$2;
|
|
58295
58309
|
const { getFileContents: getFileContents$1 } = file;
|
|
@@ -58451,7 +58465,7 @@ async function exportDashboardConfig$1(
|
|
|
58451
58465
|
.replace(/\s+/g, "-")
|
|
58452
58466
|
.toLowerCase();
|
|
58453
58467
|
|
|
58454
|
-
const { canceled, filePath } = await dialog.showSaveDialog(win, {
|
|
58468
|
+
const { canceled, filePath } = await dialog$1.showSaveDialog(win, {
|
|
58455
58469
|
title: "Export Dashboard as ZIP",
|
|
58456
58470
|
defaultPath: path$3.join(
|
|
58457
58471
|
app$3.getPath("desktop"),
|
|
@@ -58505,7 +58519,7 @@ async function exportDashboardConfig$1(
|
|
|
58505
58519
|
*/
|
|
58506
58520
|
async function selectDashboardFile$1(win) {
|
|
58507
58521
|
try {
|
|
58508
|
-
const { canceled, filePaths } = await dialog.showOpenDialog(win, {
|
|
58522
|
+
const { canceled, filePaths } = await dialog$1.showOpenDialog(win, {
|
|
58509
58523
|
title: "Import Dashboard Configuration",
|
|
58510
58524
|
filters: [{ name: "ZIP Archive", extensions: ["zip"] }],
|
|
58511
58525
|
properties: ["openFile"],
|
|
@@ -58612,7 +58626,7 @@ async function importDashboardConfig$1(
|
|
|
58612
58626
|
zipPath = options.filePath;
|
|
58613
58627
|
} else {
|
|
58614
58628
|
// Show file picker
|
|
58615
|
-
const { canceled, filePaths } = await dialog.showOpenDialog(win, {
|
|
58629
|
+
const { canceled, filePaths } = await dialog$1.showOpenDialog(win, {
|
|
58616
58630
|
title: "Import Dashboard Configuration",
|
|
58617
58631
|
filters: [{ name: "ZIP Archive", extensions: ["zip"] }],
|
|
58618
58632
|
properties: ["openFile"],
|
|
@@ -59960,7 +59974,7 @@ async function prepareDashboardForPublish$1(
|
|
|
59960
59974
|
|
|
59961
59975
|
// 9. Show save dialog for the publish package
|
|
59962
59976
|
const sanitizedName = manifest.name;
|
|
59963
|
-
const { canceled, filePath } = await dialog.showSaveDialog(win, {
|
|
59977
|
+
const { canceled, filePath } = await dialog$1.showSaveDialog(win, {
|
|
59964
59978
|
title: "Save Dashboard Package for Registry",
|
|
59965
59979
|
defaultPath: path$3.join(
|
|
59966
59980
|
app$3.getPath("desktop"),
|
|
@@ -61458,6 +61472,133 @@ function buildGrantsListing$1(
|
|
|
61458
61472
|
|
|
61459
61473
|
var widgetMcpGrantsListing = { buildGrantsListing: buildGrantsListing$1 };
|
|
61460
61474
|
|
|
61475
|
+
/**
|
|
61476
|
+
* grantDiff.js
|
|
61477
|
+
*
|
|
61478
|
+
* Pure-function diff between two grant blobs. Used by the
|
|
61479
|
+
* `widget-mcp:set-grant` IPC handler to decide whether the change
|
|
61480
|
+
* needs OS-native confirmation (broadening permissions) or can pass
|
|
61481
|
+
* through silently (revocations / equal / narrowing).
|
|
61482
|
+
*
|
|
61483
|
+
* Returns { broadening: boolean, summary: string[] }. `summary` is a
|
|
61484
|
+
* list of human-readable additions used to populate the native
|
|
61485
|
+
* confirm dialog.
|
|
61486
|
+
*
|
|
61487
|
+
* Broadening dimensions checked:
|
|
61488
|
+
* - servers: new server name present in newGrant.servers but not in
|
|
61489
|
+
* currentGrant.servers
|
|
61490
|
+
* - server tools: new tool name in an existing server's `tools[]`
|
|
61491
|
+
* - server paths: new entry in `readPaths[]` or `writePaths[]`
|
|
61492
|
+
* (including `*` wildcard added)
|
|
61493
|
+
* - domains.fs: new block, or new `readPaths[]` / `writePaths[]`
|
|
61494
|
+
* entry within an existing block (including `*`)
|
|
61495
|
+
* - domains.network: new block, or new `hosts[]` entry (including
|
|
61496
|
+
* `*` and `*.<base>` wildcards)
|
|
61497
|
+
*
|
|
61498
|
+
* Reductions, equality, and "no permission" → "no permission"
|
|
61499
|
+
* transitions are NOT broadening.
|
|
61500
|
+
*/
|
|
61501
|
+
|
|
61502
|
+
function _arr(x) {
|
|
61503
|
+
return Array.isArray(x) ? x : [];
|
|
61504
|
+
}
|
|
61505
|
+
|
|
61506
|
+
function _added(currentList, newList) {
|
|
61507
|
+
const cur = new Set(_arr(currentList));
|
|
61508
|
+
const out = [];
|
|
61509
|
+
for (const item of _arr(newList)) {
|
|
61510
|
+
if (!cur.has(item)) out.push(item);
|
|
61511
|
+
}
|
|
61512
|
+
return out;
|
|
61513
|
+
}
|
|
61514
|
+
|
|
61515
|
+
function _diffServer(serverName, currentSrv, newSrv) {
|
|
61516
|
+
const summary = [];
|
|
61517
|
+
const cur = currentSrv || {};
|
|
61518
|
+
const nxt = newSrv || {};
|
|
61519
|
+
|
|
61520
|
+
for (const tool of _added(cur.tools, nxt.tools)) {
|
|
61521
|
+
summary.push(`server "${serverName}" tool "${tool}"`);
|
|
61522
|
+
}
|
|
61523
|
+
for (const p of _added(cur.readPaths, nxt.readPaths)) {
|
|
61524
|
+
summary.push(`server "${serverName}" readPath "${p}"`);
|
|
61525
|
+
}
|
|
61526
|
+
for (const p of _added(cur.writePaths, nxt.writePaths)) {
|
|
61527
|
+
summary.push(`server "${serverName}" writePath "${p}"`);
|
|
61528
|
+
}
|
|
61529
|
+
return summary;
|
|
61530
|
+
}
|
|
61531
|
+
|
|
61532
|
+
function _diffServers(curServers, nxtServers) {
|
|
61533
|
+
const summary = [];
|
|
61534
|
+
const cur = curServers || {};
|
|
61535
|
+
const nxt = nxtServers || {};
|
|
61536
|
+
for (const name of Object.keys(nxt)) {
|
|
61537
|
+
if (!cur[name]) {
|
|
61538
|
+
// Whole new server entry → list each component as broadening.
|
|
61539
|
+
const srv = nxt[name];
|
|
61540
|
+
const tools = _arr(srv?.tools);
|
|
61541
|
+
const reads = _arr(srv?.readPaths);
|
|
61542
|
+
const writes = _arr(srv?.writePaths);
|
|
61543
|
+
if (tools.length === 0 && reads.length === 0 && writes.length === 0) {
|
|
61544
|
+
// Empty-shell server entry — no actual permissions added.
|
|
61545
|
+
// Skip; not a meaningful broadening.
|
|
61546
|
+
continue;
|
|
61547
|
+
}
|
|
61548
|
+
summary.push(`new server "${name}"`);
|
|
61549
|
+
for (const t of tools) summary.push(` tool "${t}"`);
|
|
61550
|
+
for (const p of reads) summary.push(` readPath "${p}"`);
|
|
61551
|
+
for (const p of writes) summary.push(` writePath "${p}"`);
|
|
61552
|
+
} else {
|
|
61553
|
+
summary.push(..._diffServer(name, cur[name], nxt[name]));
|
|
61554
|
+
}
|
|
61555
|
+
}
|
|
61556
|
+
return summary;
|
|
61557
|
+
}
|
|
61558
|
+
|
|
61559
|
+
function _diffDomainsFs(curFs, nxtFs) {
|
|
61560
|
+
const summary = [];
|
|
61561
|
+
const cur = curFs || {};
|
|
61562
|
+
const nxt = nxtFs || {};
|
|
61563
|
+
for (const p of _added(cur.readPaths, nxt.readPaths)) {
|
|
61564
|
+
summary.push(`fs readPath "${p}"`);
|
|
61565
|
+
}
|
|
61566
|
+
for (const p of _added(cur.writePaths, nxt.writePaths)) {
|
|
61567
|
+
summary.push(`fs writePath "${p}"`);
|
|
61568
|
+
}
|
|
61569
|
+
return summary;
|
|
61570
|
+
}
|
|
61571
|
+
|
|
61572
|
+
function _diffDomainsNetwork(curNet, nxtNet) {
|
|
61573
|
+
const summary = [];
|
|
61574
|
+
const cur = curNet || {};
|
|
61575
|
+
const nxt = nxtNet || {};
|
|
61576
|
+
for (const h of _added(cur.hosts, nxt.hosts)) {
|
|
61577
|
+
summary.push(`network host "${h}"`);
|
|
61578
|
+
}
|
|
61579
|
+
return summary;
|
|
61580
|
+
}
|
|
61581
|
+
|
|
61582
|
+
/**
|
|
61583
|
+
* @param {object|null|undefined} currentGrant
|
|
61584
|
+
* @param {object|null|undefined} newGrant
|
|
61585
|
+
* @returns {{ broadening: boolean, summary: string[] }}
|
|
61586
|
+
*/
|
|
61587
|
+
function isBroadening$1(currentGrant, newGrant) {
|
|
61588
|
+
const cur = currentGrant || {};
|
|
61589
|
+
const nxt = newGrant || {};
|
|
61590
|
+
|
|
61591
|
+
const summary = [
|
|
61592
|
+
..._diffServers(cur.servers, nxt.servers),
|
|
61593
|
+
..._diffDomainsFs(cur?.domains?.fs, nxt?.domains?.fs),
|
|
61594
|
+
..._diffDomainsNetwork(cur?.domains?.network, nxt?.domains?.network),
|
|
61595
|
+
];
|
|
61596
|
+
|
|
61597
|
+
return { broadening: summary.length > 0, summary };
|
|
61598
|
+
}
|
|
61599
|
+
|
|
61600
|
+
var grantDiff = { isBroadening: isBroadening$1 };
|
|
61601
|
+
|
|
61461
61602
|
/**
|
|
61462
61603
|
* widgetMcpGrantsController.js
|
|
61463
61604
|
*
|
|
@@ -61473,7 +61614,7 @@ var widgetMcpGrantsListing = { buildGrantsListing: buildGrantsListing$1 };
|
|
|
61473
61614
|
* grant are also surfaced — those are the install-consent retroactive prompts.
|
|
61474
61615
|
*/
|
|
61475
61616
|
|
|
61476
|
-
const { ipcMain: ipcMain$1 } = require$$0$1;
|
|
61617
|
+
const { ipcMain: ipcMain$1, dialog, BrowserWindow: BrowserWindow$1 } = require$$0$1;
|
|
61477
61618
|
const {
|
|
61478
61619
|
getGrant,
|
|
61479
61620
|
setGrant,
|
|
@@ -61484,13 +61625,62 @@ const {
|
|
|
61484
61625
|
const { getWidgetMcpPermissions } = widgetPermissions;
|
|
61485
61626
|
const { getWidgetRegistry } = widgetRegistryExports;
|
|
61486
61627
|
const { buildGrantsListing } = widgetMcpGrantsListing;
|
|
61628
|
+
const { isBroadening } = grantDiff;
|
|
61629
|
+
|
|
61630
|
+
// Native confirm dialog for any set-grant call that broadens the
|
|
61631
|
+
// widget's current permissions. The dialog runs at OS level — a
|
|
61632
|
+
// renderer (including a malicious widget) cannot dismiss it
|
|
61633
|
+
// programmatically. This is the defense-in-depth fix for the
|
|
61634
|
+
// `widget-mcp:set-grant` consent-bypass gap documented in the IPC
|
|
61635
|
+
// audit doc: a widget calling `mainApi.widgetMcp.setGrant("@self",
|
|
61636
|
+
// {wide-open perms})` now triggers a system-level prompt the user
|
|
61637
|
+
// must explicitly approve. Reductions / equality pass unprompted.
|
|
61638
|
+
async function _confirmBroadening(event, widgetId, summary) {
|
|
61639
|
+
const senderWindow =
|
|
61640
|
+
BrowserWindow$1.fromWebContents(event.sender) ||
|
|
61641
|
+
BrowserWindow$1.getFocusedWindow();
|
|
61642
|
+
// Cap the listed lines so the dialog body stays readable.
|
|
61643
|
+
const MAX_LINES = 20;
|
|
61644
|
+
const trimmed = summary.slice(0, MAX_LINES);
|
|
61645
|
+
const overflow =
|
|
61646
|
+
summary.length > MAX_LINES
|
|
61647
|
+
? `\n …and ${summary.length - MAX_LINES} more`
|
|
61648
|
+
: "";
|
|
61649
|
+
const detail =
|
|
61650
|
+
"Widget '" +
|
|
61651
|
+
widgetId +
|
|
61652
|
+
"' will be granted the following NEW permissions:\n\n " +
|
|
61653
|
+
trimmed.join("\n ") +
|
|
61654
|
+
overflow +
|
|
61655
|
+
"\n\nIf you didn't initiate this from Settings → Privacy & Security, " +
|
|
61656
|
+
"click Cancel — a malicious widget may be trying to escalate its own " +
|
|
61657
|
+
"permissions.";
|
|
61658
|
+
|
|
61659
|
+
const result = await dialog.showMessageBox(senderWindow, {
|
|
61660
|
+
type: "warning",
|
|
61661
|
+
title: "Confirm permissions change",
|
|
61662
|
+
message: "Allow new permissions for " + widgetId + "?",
|
|
61663
|
+
detail,
|
|
61664
|
+
buttons: ["Cancel", "Allow"],
|
|
61665
|
+
defaultId: 0,
|
|
61666
|
+
cancelId: 0,
|
|
61667
|
+
noLink: true,
|
|
61668
|
+
});
|
|
61669
|
+
return result.response === 1;
|
|
61670
|
+
}
|
|
61487
61671
|
|
|
61488
61672
|
function setupWidgetMcpGrantsHandlers() {
|
|
61489
61673
|
ipcMain$1.handle("widget-mcp:get-grant", (event, widgetId) => {
|
|
61490
61674
|
return getGrant(widgetId);
|
|
61491
61675
|
});
|
|
61492
61676
|
|
|
61493
|
-
ipcMain$1.handle("widget-mcp:set-grant", (event, widgetId, perms) => {
|
|
61677
|
+
ipcMain$1.handle("widget-mcp:set-grant", async (event, widgetId, perms) => {
|
|
61678
|
+
const current = getGrant(widgetId);
|
|
61679
|
+
const diff = isBroadening(current, perms);
|
|
61680
|
+
if (diff.broadening) {
|
|
61681
|
+
const approved = await _confirmBroadening(event, widgetId, diff.summary);
|
|
61682
|
+
if (!approved) return false;
|
|
61683
|
+
}
|
|
61494
61684
|
return setGrant(widgetId, perms);
|
|
61495
61685
|
});
|
|
61496
61686
|
|