@electron-memory/monitor 0.2.2 → 0.2.3

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/index.mjs CHANGED
@@ -1073,9 +1073,10 @@ import { BrowserWindow as BrowserWindow2 } from "electron";
1073
1073
  import * as path3 from "path";
1074
1074
 
1075
1075
  // src/core/dashboard-protocol.ts
1076
- import { app as app2, protocol } from "electron";
1076
+ import { app as app2, net, protocol } from "electron";
1077
1077
  import { readFile } from "fs/promises";
1078
1078
  import * as path2 from "path";
1079
+ import { pathToFileURL } from "url";
1079
1080
  var SCHEME = "emm-dashboard";
1080
1081
  var privilegedRegistered = false;
1081
1082
  var handlerRegistered = false;
@@ -1110,6 +1111,48 @@ function isPathInsideRoot(filePath, root) {
1110
1111
  }
1111
1112
  return true;
1112
1113
  }
1114
+ function urlToUiRelativePath(requestUrl) {
1115
+ let u;
1116
+ try {
1117
+ u = new URL(requestUrl);
1118
+ } catch {
1119
+ return "";
1120
+ }
1121
+ let p = "";
1122
+ try {
1123
+ p = decodeURIComponent(u.pathname || "");
1124
+ } catch {
1125
+ p = u.pathname || "";
1126
+ }
1127
+ p = p.replace(/^\/+/, "");
1128
+ const host = (u.hostname || "").toLowerCase();
1129
+ if (p.includes("..")) {
1130
+ return "";
1131
+ }
1132
+ if (host === "electron" || host === "") {
1133
+ return p || "index.html";
1134
+ }
1135
+ if (p) {
1136
+ return `${host}/${p}`.replace(/\\/g, "/");
1137
+ }
1138
+ if (host.includes(".")) {
1139
+ return host;
1140
+ }
1141
+ return "index.html";
1142
+ }
1143
+ function bufferToResponseBody(buf) {
1144
+ return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
1145
+ }
1146
+ function looksLikeHtml(buf) {
1147
+ let i = 0;
1148
+ if (buf.length >= 3 && buf[0] === 239 && buf[1] === 187 && buf[2] === 191) {
1149
+ i = 3;
1150
+ }
1151
+ while (i < buf.length && (buf[i] === 32 || buf[i] === 9 || buf[i] === 10 || buf[i] === 13)) {
1152
+ i += 1;
1153
+ }
1154
+ return i < buf.length && buf[i] === 60;
1155
+ }
1113
1156
  function corsHeaders() {
1114
1157
  return {
1115
1158
  "Access-Control-Allow-Origin": "*",
@@ -1151,24 +1194,40 @@ function ensureDashboardProtocolHandler(uiRoot) {
1151
1194
  return new Response(null, { status: 204, headers: corsHeaders() });
1152
1195
  }
1153
1196
  try {
1154
- let pathname;
1155
- try {
1156
- pathname = decodeURIComponent(new URL(request.url).pathname);
1157
- } catch {
1158
- return new Response("Bad URL", { status: 400, headers: corsHeaders() });
1197
+ const rel = urlToUiRelativePath(request.url);
1198
+ if (!rel || rel.includes("..")) {
1199
+ return new Response("Bad path", { status: 400, headers: corsHeaders() });
1159
1200
  }
1160
- let rel = pathname.replace(/^\/+/, "");
1161
- if (!rel) {
1162
- rel = "index.html";
1163
- }
1164
- const filePath = path2.resolve(path2.join(base, rel));
1201
+ const filePath = path2.resolve(path2.join(base, ...rel.split("/")));
1165
1202
  if (!isPathInsideRoot(filePath, base)) {
1166
1203
  return new Response("Forbidden", { status: 403, headers: corsHeaders() });
1167
1204
  }
1168
- const body = await readFile(filePath);
1205
+ const fileUrl = pathToFileURL(filePath).href;
1206
+ let upstream;
1207
+ try {
1208
+ upstream = await net.fetch(fileUrl);
1209
+ } catch {
1210
+ upstream = new Response(null, { status: 599 });
1211
+ }
1212
+ let buf;
1213
+ if (!upstream.ok) {
1214
+ buf = await readFile(filePath);
1215
+ } else {
1216
+ const ab = await upstream.arrayBuffer();
1217
+ buf = Buffer.from(ab);
1218
+ }
1169
1219
  const ext = path2.extname(filePath).toLowerCase();
1220
+ if (ext === ".html" && !looksLikeHtml(buf)) {
1221
+ console.error(
1222
+ "[@electron-memory/monitor] emm-dashboard: not HTML at",
1223
+ filePath,
1224
+ "url=",
1225
+ request.url
1226
+ );
1227
+ return new Response("Invalid dashboard HTML", { status: 500, headers: corsHeaders() });
1228
+ }
1170
1229
  const mime = MIME_BY_EXT[ext] || "application/octet-stream";
1171
- return new Response(body, {
1230
+ return new Response(bufferToResponseBody(buf), {
1172
1231
  status: 200,
1173
1232
  headers: {
1174
1233
  "Content-Type": mime,
@@ -1178,6 +1237,7 @@ function ensureDashboardProtocolHandler(uiRoot) {
1178
1237
  });
1179
1238
  } catch (err) {
1180
1239
  const msg = err instanceof Error ? err.message : String(err);
1240
+ console.error("[@electron-memory/monitor] emm-dashboard:", request.url, err);
1181
1241
  return new Response(msg, { status: 404, headers: corsHeaders() });
1182
1242
  }
1183
1243
  });