home-hosted 0.7.6 → 0.7.8

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/cli.js CHANGED
@@ -2437,6 +2437,11 @@ function createAuthRoute(deps) {
2437
2437
  statusCode: 400,
2438
2438
  code: "EXPOSED_WITHOUT_PASSWORD"
2439
2439
  });
2440
+ const proxyExposure = checkProxyExposure(deps.proxy.config, false, false, false);
2441
+ if (proxyExposure !== null) throw new DetailedError(proxyExposure, {
2442
+ statusCode: 400,
2443
+ code: "PROXY_EXPOSURE_BLOCKED"
2444
+ });
2440
2445
  deps.auth.clearPassword();
2441
2446
  deps.panel.settings.updateControl({ auth: { enabled: false } });
2442
2447
  return c.json({ ok: true });
@@ -6028,9 +6033,20 @@ var init_logs = __esmMin((() => {
6028
6033
  }));
6029
6034
  //#endregion
6030
6035
  //#region src/api/metrics.ts
6036
+ /**
6037
+ * Escapes a value for the quoted form of a Prometheus label, per the exposition
6038
+ * format: only `\\`, `"` and a newline are special, and each needs a backslash.
6039
+ *
6040
+ * A raw Windows path is the reachable case — `diskPaths` is user config and
6041
+ * `C:\Users\me` emits `\U`, which is not a valid escape, so the scraper rejects the
6042
+ * whole sample. The ids are charset-constrained by their schemas, but the paths are not.
6043
+ */
6044
+ function labelValue(value) {
6045
+ return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n");
6046
+ }
6031
6047
  /** A server id repeats across workspaces, so every sample names both. */
6032
6048
  function labels(server) {
6033
- return `server="${server.id}",workspace="${server.workspaceId ?? ""}"`;
6049
+ return `server="${labelValue(server.id)}",workspace="${labelValue(server.workspaceId ?? "")}"`;
6034
6050
  }
6035
6051
  /**
6036
6052
  * Prometheus text for whatever wants to scrape the panel (Beszel, Grafana,
@@ -6061,7 +6077,7 @@ function createMetricsRoute(deps) {
6061
6077
  metric("hh_server_response_ms", "Last health probe latency in milliseconds", servers.filter((server) => server.responseMs !== null).map((server) => `hh_server_response_ms{${labels(server)}} ${server.responseMs}`));
6062
6078
  metric("hh_server_rss_bytes", "RSS of the server process tree", servers.filter((server) => server.resources?.rssBytes != null).map((server) => `hh_server_rss_bytes{${labels(server)}} ${server.resources.rssBytes}`));
6063
6079
  metric("hh_server_cpu_percent", "CPU percent of the server process tree", servers.filter((server) => server.resources?.cpuPercent != null).map((server) => `hh_server_cpu_percent{${labels(server)}} ${server.resources.cpuPercent}`));
6064
- metric("hh_host_disk_used_percent", "Disk usage percent per configured path", host.disks.map((disk) => `hh_host_disk_used_percent{mount="${disk.path}"} ${disk.usedPercent.toFixed(2)}`));
6080
+ metric("hh_host_disk_used_percent", "Disk usage percent per configured path", host.disks.map((disk) => `hh_host_disk_used_percent{mount="${labelValue(disk.path)}"} ${disk.usedPercent.toFixed(2)}`));
6065
6081
  metric("hh_host_memory_used_percent", "Memory usage percent", [`hh_host_memory_used_percent ${host.memoryUsedPercent.toFixed(2)}`]);
6066
6082
  metric("hh_host_swap_used_percent", "Swap usage percent", [`hh_host_swap_used_percent ${host.swapUsedPercent.toFixed(2)}`]);
6067
6083
  metric("hh_host_load1_per_cpu", "1 minute load average per cpu", [`hh_host_load1_per_cpu ${((host.loadAvg[0] ?? 0) / Math.max(1, host.cpus)).toFixed(3)}`]);
@@ -6561,7 +6577,29 @@ function splitCommandLine(line) {
6561
6577
  let current = "";
6562
6578
  let quoted = false;
6563
6579
  let started = false;
6564
- for (const char of line.trim()) {
6580
+ const chars = [...line.trim()];
6581
+ for (let index = 0; index < chars.length; index += 1) {
6582
+ const char = chars[index];
6583
+ if (char === "\\") {
6584
+ let run = 0;
6585
+ while (chars[index + run] === "\\") run += 1;
6586
+ if (chars[index + run] !== "\"") {
6587
+ current += "\\".repeat(run);
6588
+ started = true;
6589
+ index += run - 1;
6590
+ continue;
6591
+ }
6592
+ current += "\\".repeat(Math.floor(run / 2));
6593
+ started = true;
6594
+ if (run % 2 === 1) {
6595
+ current += "\"";
6596
+ index += run;
6597
+ continue;
6598
+ }
6599
+ quoted = !quoted;
6600
+ index += run;
6601
+ continue;
6602
+ }
6565
6603
  if (char === "\"") {
6566
6604
  quoted = !quoted;
6567
6605
  started = true;
@@ -9425,7 +9463,7 @@ function createServersRoute(deps) {
9425
9463
  responses: {
9426
9464
  201: {
9427
9465
  description: "Created",
9428
- content: jsonBody(serverResponse)
9466
+ content: jsonBody(storedServerResponse)
9429
9467
  },
9430
9468
  400: ERROR_RESPONSES[400]
9431
9469
  }
@@ -9618,7 +9656,7 @@ function createServersRoute(deps) {
9618
9656
  responses: {
9619
9657
  200: {
9620
9658
  description: "The server",
9621
- content: jsonBody(serverResponse)
9659
+ content: jsonBody(storedServerResponse)
9622
9660
  },
9623
9661
  400: ERROR_RESPONSES[400],
9624
9662
  404: ERROR_RESPONSES[404]
@@ -9660,7 +9698,7 @@ function createServersRoute(deps) {
9660
9698
  }
9661
9699
  });
9662
9700
  }
9663
- var idParam$1, MAX_PENDING_WRITES, serverResponse, serversResponse, okResponse, bufferedLogsQuery;
9701
+ var idParam$1, MAX_PENDING_WRITES, serverResponse, serversResponse, storedServerResponse, okResponse, bufferedLogsQuery;
9664
9702
  var init_$_routes$1 = __esmMin((() => {
9665
9703
  init_store();
9666
9704
  init_action_result();
@@ -9674,6 +9712,7 @@ var init_$_routes$1 = __esmMin((() => {
9674
9712
  MAX_PENDING_WRITES = 200;
9675
9713
  serverResponse = type({ server: serverViewSchema });
9676
9714
  serversResponse = type({ servers: serverViewSchema.array() });
9715
+ storedServerResponse = type({ server: serverSchema });
9677
9716
  okResponse = type({ ok: "boolean" });
9678
9717
  bufferedLogsQuery = logQuerySchema.merge(workspaceQuerySchema);
9679
9718
  }));
@@ -9736,6 +9775,11 @@ function createSettingsRoute(deps) {
9736
9775
  statusCode: 400,
9737
9776
  code: "EXPOSURE_BLOCKED"
9738
9777
  });
9778
+ const proxyExposure = checkProxyExposure(deps.proxy.config, patch.control?.auth?.enabled ?? current.auth.enabled, deps.auth.passwordSet, deps.auth.usingDefaultPassword);
9779
+ if (proxyExposure !== null) throw new DetailedError(proxyExposure, {
9780
+ statusCode: 400,
9781
+ code: "PROXY_EXPOSURE_BLOCKED"
9782
+ });
9739
9783
  const previous = {
9740
9784
  trustProxy: current.auth.trustProxy,
9741
9785
  tlsEnabled: current.tls.enabled