summrise-agent 1.2.509 → 1.2.511
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/bin/summrise.exe +0 -0
- package/package.json +11 -8
- package/summrise-agent.exe +0 -0
- package/summrise-desktop-electron/src/main.js +400 -354
- package/summrise-desktop-electron/src/preload.js +4 -1
- package/summrise-desktop-electron/src/summrise_shell_policy.js +1280 -0
- package/summrise-desktop-electron/src/summrise_shell_policy_bg.wasm +0 -0
- package/summrise-desktop-electron/src/summrise_url_policy.js +404 -0
- package/summrise-desktop-electron/src/summrise_url_policy_bg.wasm +0 -0
- package/summrise-launch.exe +0 -0
- package/bin/summrise.js +0 -3906
- package/summrise-desktop-electron/src/url-policy.js +0 -238
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// Pure URL/origin policy for the desktop shell — extracted from main.ts so
|
|
3
|
-
// the security predicates carry unit tests (main.ts imports electron and is
|
|
4
|
-
// unimportable under plain node). SHIPPED ALONGSIDE main.js: the npm build
|
|
5
|
-
// chain copies it, and `summrise update` stages + swaps it with main/preload.
|
|
6
|
-
//
|
|
7
|
-
// IPC audit #1 (HIGH): startsWith(BASE) was BYPASSABLE — the string
|
|
8
|
-
// 'http://127.0.0.1:18080@evil.com/x' passes the prefix test but Chromium
|
|
9
|
-
// parses its host as evil.com (userinfo trick). Every origin decision here
|
|
10
|
-
// compares the PARSED origin.
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.BASE_ORIGIN = exports.BASE = void 0;
|
|
13
|
-
exports.setAgentPort = setAgentPort;
|
|
14
|
-
exports.getAgentPort = getAgentPort;
|
|
15
|
-
exports.agentBase = agentBase;
|
|
16
|
-
exports.parseAgentPort = parseAgentPort;
|
|
17
|
-
exports.isBaseOrigin = isBaseOrigin;
|
|
18
|
-
exports.frameUrlOk = frameUrlOk;
|
|
19
|
-
exports.controlOriginOk = controlOriginOk;
|
|
20
|
-
exports.isDesktopSpaUrl = isDesktopSpaUrl;
|
|
21
|
-
exports.isPrivateHost = isPrivateHost;
|
|
22
|
-
exports.certBypassAllowed = certBypassAllowed;
|
|
23
|
-
exports.sanitizeBrowserUrl = sanitizeBrowserUrl;
|
|
24
|
-
exports.setDshPort = setDshPort;
|
|
25
|
-
exports.getDshPort = getDshPort;
|
|
26
|
-
exports.dshBase = dshBase;
|
|
27
|
-
exports.isDshUrl = isDshUrl;
|
|
28
|
-
exports.BASE = "http://127.0.0.1:18080";
|
|
29
|
-
exports.BASE_ORIGIN = new URL(exports.BASE).origin;
|
|
30
|
-
// Configurable agent port (custom-port installs): the shell follows the
|
|
31
|
-
// agent's config.yaml server.port (main.ts resolves it at boot via
|
|
32
|
-
// SUMMRISE_AGENT_PORT env, else the registry install/data dirs). Predicates
|
|
33
|
-
// below compare against the CONFIGURED origin — a hardcoded 18080 would
|
|
34
|
-
// deafen the IPC bridge and strand the desktop on any custom port.
|
|
35
|
-
// Default stays 18080 (canonical); tests reset via setAgentPort(18080).
|
|
36
|
-
let agentPort = null;
|
|
37
|
-
function setAgentPort(port) {
|
|
38
|
-
if (Number.isInteger(port) && port > 0 && port < 65536)
|
|
39
|
-
agentPort = port;
|
|
40
|
-
}
|
|
41
|
-
function getAgentPort() {
|
|
42
|
-
return agentPort ?? 18080;
|
|
43
|
-
}
|
|
44
|
-
function agentBase() {
|
|
45
|
-
return `http://127.0.0.1:${getAgentPort()}`;
|
|
46
|
-
}
|
|
47
|
-
function agentOrigin() {
|
|
48
|
-
return new URL(agentBase()).origin;
|
|
49
|
-
}
|
|
50
|
-
// server.port out of an agent config.yaml (first `port:` inside the
|
|
51
|
-
// top-level `server:` section; null when absent/invalid). Pure so the
|
|
52
|
-
// shell's file/registry glue stays untestable-thin and this stays pinned.
|
|
53
|
-
function parseAgentPort(yamlText) {
|
|
54
|
-
let inServer = false;
|
|
55
|
-
for (const raw of String(yamlText || "").split(/\r?\n/)) {
|
|
56
|
-
const line = raw.trimEnd();
|
|
57
|
-
if (/^\S/.test(line))
|
|
58
|
-
inServer = /^server\s*:/.test(line);
|
|
59
|
-
if (!inServer)
|
|
60
|
-
continue;
|
|
61
|
-
const m = /^\s*port\s*:\s*"?(\d{1,5})"?\s*(?:#.*)?$/.exec(line);
|
|
62
|
-
if (m) {
|
|
63
|
-
const n = Number(m[1]);
|
|
64
|
-
if (Number.isInteger(n) && n > 0 && n < 65536)
|
|
65
|
-
return n;
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
function isBaseOrigin(url) {
|
|
72
|
-
try {
|
|
73
|
-
return new URL(url).origin === agentOrigin();
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
// IPC audit #2: preload runs in EVERY frame; a frame may invoke the bridge
|
|
80
|
-
// only when its URL carries the pinned origin (main.ts feeds this the raw
|
|
81
|
-
// event.senderFrame.url).
|
|
82
|
-
function frameUrlOk(url) {
|
|
83
|
-
return isBaseOrigin(url || "");
|
|
84
|
-
}
|
|
85
|
-
// THE LOOPBACK CONTROL API'S ORIGIN VETO (port 9444), and the reason it lives
|
|
86
|
-
// here: the HTTP twin of IPC audit #1 was never fixed. `main.ts` tested the
|
|
87
|
-
// caller's Origin with
|
|
88
|
-
//
|
|
89
|
-
// /^(https?:\/\/(127\.0\.0\.1|localhost)(:\d+)?|file:\/\/)/i
|
|
90
|
-
//
|
|
91
|
-
// which has NO `$` ANCHOR — so `http://127.0.0.1.evil.com`, `http://localhost.evil.com`
|
|
92
|
-
// and `http://127.0.0.1x` all passed, and the API reflects the caller's Origin,
|
|
93
|
-
// so a foreign page could READ /api/browser-session/list (session URLs) and
|
|
94
|
-
// /api/shell/icon-status (local paths) and POST /api/browser-session/open and
|
|
95
|
-
// /api/shell/start-agent. That is the SAME class as the `startsWith(BASE)` bug
|
|
96
|
-
// this module was extracted to kill; `isDesktopSpaUrl`'s own test pins the
|
|
97
|
-
// sibling-host lookalike, and the HTTP path kept the old shape because it was
|
|
98
|
-
// the one decision that never moved in here.
|
|
99
|
-
//
|
|
100
|
-
// An ABSENT Origin is allowed deliberately: the API is also driven by `curl`
|
|
101
|
-
// and native tooling, which send none, and `main.ts` documents that carve-out.
|
|
102
|
-
// This is a nuisance barrier for browser pages, NOT authentication — the IPC
|
|
103
|
-
// bridge stays separately gated on the frame's own origin.
|
|
104
|
-
function controlOriginOk(origin) {
|
|
105
|
-
if (!origin || origin === "null")
|
|
106
|
-
return true; // native tooling / the data: wait page
|
|
107
|
-
try {
|
|
108
|
-
const u = new URL(origin);
|
|
109
|
-
if (u.protocol === "file:")
|
|
110
|
-
return true;
|
|
111
|
-
if (u.protocol !== "http:" && u.protocol !== "https:")
|
|
112
|
-
return false;
|
|
113
|
-
return u.hostname === "127.0.0.1" || u.hostname === "localhost";
|
|
114
|
-
}
|
|
115
|
-
catch {
|
|
116
|
-
// Unparseable is NOT allowed: a value the policy cannot read must never be
|
|
117
|
-
// treated as one it recognises.
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
// Main-window tripwire allow-list (did-navigate backstop): the desktop SPA
|
|
122
|
-
// subtree of the base origin. STRING-PREFIX check (startsWith(BASE +
|
|
123
|
-
// "/desktop")) was the exact class IPC audit #1 flagged — compare the PARSED
|
|
124
|
-
// origin and the parsed pathname instead. data: (the wait page) and
|
|
125
|
-
// about:blank are handled by the caller.
|
|
126
|
-
function isDesktopSpaUrl(url) {
|
|
127
|
-
try {
|
|
128
|
-
const u = new URL(url);
|
|
129
|
-
if (u.origin !== agentOrigin())
|
|
130
|
-
return false;
|
|
131
|
-
// Segment semantics: /desktop and /desktop/* — /desktopx is a different
|
|
132
|
-
// path, not the SPA mount.
|
|
133
|
-
return u.pathname === "/desktop" || u.pathname.startsWith("/desktop/");
|
|
134
|
-
}
|
|
135
|
-
catch {
|
|
136
|
-
return false;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
// Lab-device TLS bypass predicate (device-caught: OpenWrt-style
|
|
140
|
-
// self-signed defaults, e.g. the GPON ONT web UI, fail every navigation
|
|
141
|
-
// with ERR_CERT_AUTHORITY_INVALID). Private-network hosts MAY bypass cert
|
|
142
|
-
// errors; the public internet keeps full validation — a bypassed MITM on
|
|
143
|
-
// a lab LAN is contained, on the open web it is not. Pure + unit-tested;
|
|
144
|
-
// main.ts feeds it the raw certificate-error URL.
|
|
145
|
-
function isPrivateHost(hostname) {
|
|
146
|
-
const h = String(hostname || "").trim().toLowerCase();
|
|
147
|
-
if (!h)
|
|
148
|
-
return false;
|
|
149
|
-
if (h === "localhost" || h === "::1" || h === "[::1]")
|
|
150
|
-
return true;
|
|
151
|
-
if (h.endsWith(".local") || h.endsWith(".local."))
|
|
152
|
-
return true;
|
|
153
|
-
const parts = h.split(".");
|
|
154
|
-
if (parts.length !== 4)
|
|
155
|
-
return false;
|
|
156
|
-
const nums = [];
|
|
157
|
-
for (const p of parts) {
|
|
158
|
-
if (!/^\d{1,3}$/.test(p))
|
|
159
|
-
return false;
|
|
160
|
-
const n = Number(p);
|
|
161
|
-
if (n > 255)
|
|
162
|
-
return false;
|
|
163
|
-
nums.push(n);
|
|
164
|
-
}
|
|
165
|
-
const [a, b] = nums;
|
|
166
|
-
if (a === 10)
|
|
167
|
-
return true;
|
|
168
|
-
if (a === 172 && b >= 16 && b <= 31)
|
|
169
|
-
return true;
|
|
170
|
-
if (a === 192 && b === 168)
|
|
171
|
-
return true;
|
|
172
|
-
if (a === 127)
|
|
173
|
-
return true;
|
|
174
|
-
if (a === 169 && b === 254)
|
|
175
|
-
return true;
|
|
176
|
-
return false;
|
|
177
|
-
}
|
|
178
|
-
// Certificate-error gate for app.on("certificate-error"): true = bypass
|
|
179
|
-
// (preventDefault + callback(true)), false = deny. http(s) URLs on
|
|
180
|
-
// private hosts only — anything else (public hosts, weird schemes,
|
|
181
|
-
// unparsable input) stays fully validated.
|
|
182
|
-
function certBypassAllowed(url) {
|
|
183
|
-
let u;
|
|
184
|
-
try {
|
|
185
|
-
u = new URL(String(url || ""));
|
|
186
|
-
}
|
|
187
|
-
catch {
|
|
188
|
-
return false;
|
|
189
|
-
}
|
|
190
|
-
if (u.protocol !== "http:" && u.protocol !== "https:")
|
|
191
|
-
return false;
|
|
192
|
-
return isPrivateHost(u.hostname);
|
|
193
|
-
}
|
|
194
|
-
// AI-opened browser windows must never reach file://, javascript: or
|
|
195
|
-
// arbitrary schemes through the CDP-driven session windows.
|
|
196
|
-
function sanitizeBrowserUrl(url) {
|
|
197
|
-
// stage-n preload audit LOW: coerce to string BEFORE trim() — a renderer
|
|
198
|
-
// passing a number/object would throw TypeError in the main process (DoS).
|
|
199
|
-
const t = String(url || "about:blank").trim();
|
|
200
|
-
if (t === "about:blank")
|
|
201
|
-
return t;
|
|
202
|
-
try {
|
|
203
|
-
const u = new URL(t);
|
|
204
|
-
if (u.protocol === "http:" || u.protocol === "https:")
|
|
205
|
-
return u.toString();
|
|
206
|
-
}
|
|
207
|
-
catch { /* fall through */ }
|
|
208
|
-
return "about:blank";
|
|
209
|
-
}
|
|
210
|
-
// THE DSH VIEW'S DOOR. The shell's SECOND embedded view shows the harness's own UI, which
|
|
211
|
-
// runs on THIS machine's loopback — so this door is one origin and nothing else, and it is a
|
|
212
|
-
// SEPARATE predicate from sanitizeBrowserUrl on purpose: the browser view's door is the open
|
|
213
|
-
// internet (an AI drives it), this one is a single port, and a door that is wide for one view
|
|
214
|
-
// must not quietly become wide for the other. The DSH view has no address bar and nothing
|
|
215
|
-
// drives it but the operator, so there is nothing here to widen it for.
|
|
216
|
-
//
|
|
217
|
-
// The port is the DSH's own (default 18081 — main.ts resolves SUMMRISE_DSH_PORT first), and
|
|
218
|
-
// it is deliberately NOT the agent's port: the agent's origin is where the panel lives, and a
|
|
219
|
-
// view that could load the panel could also load /api/* with the panel's own authority.
|
|
220
|
-
let dshPort = null;
|
|
221
|
-
function setDshPort(port) {
|
|
222
|
-
if (Number.isInteger(port) && port > 0 && port < 65536)
|
|
223
|
-
dshPort = port;
|
|
224
|
-
}
|
|
225
|
-
function getDshPort() {
|
|
226
|
-
return dshPort ?? 18081;
|
|
227
|
-
}
|
|
228
|
-
function dshBase() {
|
|
229
|
-
return `http://127.0.0.1:${getDshPort()}`;
|
|
230
|
-
}
|
|
231
|
-
function isDshUrl(url) {
|
|
232
|
-
try {
|
|
233
|
-
return new URL(url).origin === new URL(dshBase()).origin;
|
|
234
|
-
}
|
|
235
|
-
catch {
|
|
236
|
-
return false;
|
|
237
|
-
}
|
|
238
|
-
}
|