@ultimat3/render 2.0.0 → 3.0.0

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.
Files changed (2) hide show
  1. package/package.json +5 -5
  2. package/src/head.ts +13 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultimat3/render",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "The route primitive and the five render modes: static, isr, ssr, stream, spa.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -31,10 +31,10 @@
31
31
  "test": "bun test"
32
32
  },
33
33
  "dependencies": {
34
- "@ultimat3/cache": "2.0.0",
35
- "@ultimat3/core": "2.0.0",
36
- "@ultimat3/i18n": "2.0.0",
37
- "@ultimat3/seo": "2.0.0",
34
+ "@ultimat3/cache": "3.0.0",
35
+ "@ultimat3/core": "3.0.0",
36
+ "@ultimat3/i18n": "3.0.0",
37
+ "@ultimat3/seo": "3.0.0",
38
38
  "sass": "1.102.0"
39
39
  }
40
40
  }
package/src/head.ts CHANGED
@@ -134,10 +134,20 @@ function contentOf(tag: HeadTag, raw: string): string {
134
134
  return carriesJson(tag) ? escapeJsonContent(raw) : escapeRawTextContent(raw);
135
135
  }
136
136
 
137
- /** `application/ld+json`, `application/json`, any `…+json`: the body is data, not code. */
137
+ /**
138
+ * `application/ld+json`, `application/json`, any `…+json`: the body is data, not code.
139
+ *
140
+ * The MIME parameter is cut before the suffix test, because a real document writes
141
+ * `type="application/ld+json; charset=utf-8"` and that does not end in `json` — so the block built
142
+ * from route data, which is the path attacker text takes, silently took the raw-text escaper
143
+ * instead of the total one. `</` is escaped either way, so this was a weakened boundary rather than
144
+ * a break-out; the JSON rule is total on purpose, and a `charset` is not a reason to leave it.
145
+ */
138
146
  function carriesJson(tag: HeadTag): boolean {
139
- const type = tag.attrs?.['type'];
140
- return typeof type === 'string' && type.trim().toLowerCase().endsWith('json');
147
+ const declared = tag.attrs?.['type'];
148
+ if (typeof declared !== 'string') return false;
149
+ const [type = ''] = declared.split(';');
150
+ return type.trim().toLowerCase().endsWith('json');
141
151
  }
142
152
 
143
153
  export interface ThemeScriptOptions {