@floegence/flowersec-core 0.19.2 → 0.19.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.
@@ -13,6 +13,28 @@ function normalizePathPrefix(name, v) {
13
13
  throw new Error(`${name} must not include scheme/host`);
14
14
  return s;
15
15
  }
16
+ function normalizeRootRelativeScriptURL(name, v) {
17
+ if (typeof v !== "string")
18
+ throw new Error(`${name} must be a string`);
19
+ const s = v.trim();
20
+ if (s === "")
21
+ throw new Error(`${name} must be non-empty`);
22
+ if (s !== v)
23
+ throw new Error(`${name} must not contain leading or trailing whitespace`);
24
+ if (!s.startsWith("/"))
25
+ throw new Error(`${name} must start with "/"`);
26
+ if (s.startsWith("//"))
27
+ throw new Error(`${name} must not start with "//"`);
28
+ if (s.includes("://"))
29
+ throw new Error(`${name} must not include scheme/host`);
30
+ if (s.includes("\\"))
31
+ throw new Error(`${name} must not contain backslash`);
32
+ if (/[\s\u0000-\u001f\u007f]/.test(s))
33
+ throw new Error(`${name} must not contain whitespace or control characters`);
34
+ if (/[<>"'`]/.test(s))
35
+ throw new Error(`${name} must not contain HTML attribute delimiters`);
36
+ return s;
37
+ }
16
38
  function normalizePathList(name, input) {
17
39
  const out = [];
18
40
  if (input == null || input.length === 0)
@@ -116,13 +138,9 @@ export function createProxyServiceWorkerScript(opts = {}) {
116
138
  }
117
139
  }
118
140
  else {
119
- injectScriptUrl =
120
- "scriptUrl" in injectHTML && typeof injectHTML.scriptUrl === "string"
121
- ? injectHTML.scriptUrl.trim()
122
- : "";
123
- if (injectScriptUrl === "") {
141
+ if (!("scriptUrl" in injectHTML))
124
142
  throw new Error("injectHTML.scriptUrl must be non-empty");
125
- }
143
+ injectScriptUrl = normalizeRootRelativeScriptURL("injectHTML.scriptUrl", injectHTML.scriptUrl);
126
144
  }
127
145
  }
128
146
  return `// Generated by @floegence/flowersec-core/proxy
@@ -315,16 +333,16 @@ function injectBootstrap(html) {
315
333
  } else if (INJECT_MODE === "external_module") {
316
334
  snippet =
317
335
  '<script type="module" src="' +
318
- INJECT_SCRIPT_URL +
336
+ escapeHTMLAttributeValue(INJECT_SCRIPT_URL) +
319
337
  '"' +
320
- (RUNTIME_GLOBAL ? ' data-flowersec-runtime-global="' + RUNTIME_GLOBAL + '"' : "") +
338
+ (RUNTIME_GLOBAL ? ' data-flowersec-runtime-global="' + escapeHTMLAttributeValue(RUNTIME_GLOBAL) + '"' : "") +
321
339
  "></script>";
322
340
  } else if (INJECT_MODE === "external_script") {
323
341
  snippet =
324
342
  '<script src="' +
325
- INJECT_SCRIPT_URL +
343
+ escapeHTMLAttributeValue(INJECT_SCRIPT_URL) +
326
344
  '"' +
327
- (RUNTIME_GLOBAL ? ' data-flowersec-runtime-global="' + RUNTIME_GLOBAL + '"' : "") +
345
+ (RUNTIME_GLOBAL ? ' data-flowersec-runtime-global="' + escapeHTMLAttributeValue(RUNTIME_GLOBAL) + '"' : "") +
328
346
  "></script>";
329
347
  }
330
348
 
@@ -341,6 +359,21 @@ function injectBootstrap(html) {
341
359
  return snippet + html;
342
360
  }
343
361
 
362
+ function escapeHTMLAttributeValue(value) {
363
+ return String(value).replace(/[&<>"'\`=]/g, (ch) => {
364
+ switch (ch) {
365
+ case "&": return "&amp;";
366
+ case "<": return "&lt;";
367
+ case ">": return "&gt;";
368
+ case '"': return "&quot;";
369
+ case "'": return "&#39;";
370
+ case "\`": return "&#96;";
371
+ case "=": return "&#61;";
372
+ default: return ch;
373
+ }
374
+ });
375
+ }
376
+
344
377
  self.addEventListener("fetch", (event) => {
345
378
  const url = new URL(event.request.url);
346
379
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floegence/flowersec-core",
3
- "version": "0.19.2",
3
+ "version": "0.19.3",
4
4
  "description": "Flowersec core TypeScript library (browser-friendly E2EE + multiplexing over WebSocket).",
5
5
  "license": "MIT",
6
6
  "repository": {