ghtml 1.7.2 → 2.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.
package/README.md CHANGED
@@ -12,7 +12,7 @@ npm i ghtml
12
12
 
13
13
  ### `html`
14
14
 
15
- The `html` function is designed to tag template literals and automatically escape their expressions to prevent XSS attacks. To intentionally bypass escaping for a specific expression, prefix it with `!`.
15
+ The `html` function is designed to tag template literals and automatically escape their expressions. To intentionally bypass escaping a specific expression, prefix it with `!`.
16
16
 
17
17
  ### `htmlGenerator`
18
18
 
@@ -32,7 +32,7 @@ Because they return generators instead of strings, a key difference of `htmlGene
32
32
 
33
33
  ### `includeFile`
34
34
 
35
- Available for Node.js users, the `includeFile` function is a wrapper around `readFileSync`. It reads and outputs the content of a file while also caching it in memory for faster future reuse.
35
+ Available in Node.js, the `includeFile` function is a wrapper around `readFileSync`. It reads and outputs the content of a file while also caching it in memory for faster future reuse.
36
36
 
37
37
  ## Usage
38
38
 
@@ -41,11 +41,11 @@ Available for Node.js users, the `includeFile` function is a wrapper around `rea
41
41
  ```js
42
42
  import { html } from "ghtml";
43
43
 
44
- const username = '<img src="https://example.com/hacker.png">';
44
+ const username = '<img src="https://example.com/pwned.png">';
45
45
  const greeting = html`<h1>Hello, ${username}!</h1>`;
46
46
 
47
47
  console.log(greeting);
48
- // Output: <h1>Hello, &lt;img src=&quot;https://example.com/hacker.png&quot;&gt;</h1>
48
+ // Output: <h1>Hello, &#60;img src=&#34;https://example.com/pwned.png&#34;&#62;</h1>
49
49
  ```
50
50
 
51
51
  To bypass escaping:
@@ -168,3 +168,7 @@ const logo = includeFile("static/logo.svg");
168
168
  console.log(logo);
169
169
  // Output: content of "static/logo.svg"
170
170
  ```
171
+
172
+ ## Security
173
+
174
+ Like [similar](https://handlebarsjs.com/guide/#html-escaping) [tools](https://github.com/mde/ejs/blob/main/SECURITY.md#out-of-scope-vulnerabilities), `ghtml` will not prevent all kinds of XSS attacks. It is the responsibility of consumers to sanitize user inputs. Some inherently insecure uses include dynamically generating JavaScript, failing to quote HTML attribute values (especially when they contain expressions), and using unsanitized user-provided URLs.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Replace your template engine with fast JavaScript by leveraging the power of tagged templates.",
4
4
  "author": "Gürgün Dayıoğlu",
5
5
  "license": "MIT",
6
- "version": "1.7.2",
6
+ "version": "2.0.0",
7
7
  "type": "module",
8
8
  "main": "./src/index.js",
9
9
  "exports": {
@@ -22,7 +22,7 @@
22
22
  "devDependencies": {
23
23
  "@fastify/pre-commit": "^2.1.0",
24
24
  "c8": "^9.1.0",
25
- "grules": "^0.17.1",
25
+ "grules": "^0.17.2",
26
26
  "tinybench": "^2.8.0"
27
27
  },
28
28
  "repository": {
package/src/html.js CHANGED
@@ -1,9 +1,10 @@
1
1
  const escapeDictionary = {
2
- '"': "&quot;",
3
- "'": "&apos;",
4
- "&": "&amp;",
5
- "<": "&lt;",
6
- ">": "&gt;",
2
+ '"': "&#34;",
3
+ "&": "&#38;",
4
+ "'": "&#39;",
5
+ "<": "&#60;",
6
+ ">": "&#62;",
7
+ "`": "&#96;",
7
8
  };
8
9
 
9
10
  const escapeRegExp = new RegExp(
@@ -19,6 +20,7 @@ const escapeFunction = (string) => {
19
20
 
20
21
  do {
21
22
  const escapedCharacter = escapeDictionary[string[end++]];
23
+
22
24
  if (escapedCharacter) {
23
25
  escaped += string.slice(start, end - 1) + escapedCharacter;
24
26
  start = end;
package/test/index.js CHANGED
@@ -59,14 +59,14 @@ test("renders safe content", () => {
59
59
  test("renders unsafe content", () => {
60
60
  assert.strictEqual(
61
61
  html`<p>${descriptionUnsafe}</p>`,
62
- `<p>&lt;script&gt;alert(&apos;This is an unsafe description.&apos;)&lt;/script&gt;</p>`,
62
+ `<p>&#60;script&#62;alert(&#39;This is an unsafe description.&#39;)&#60;/script&#62;</p>`,
63
63
  );
64
64
  });
65
65
 
66
66
  test("renders arrays", () => {
67
67
  assert.strictEqual(
68
68
  html`<p>${[descriptionSafe, descriptionUnsafe]}</p>`,
69
- "<p>This is a safe description.&lt;script&gt;alert(&apos;This is an unsafe description.&apos;)&lt;/script&gt;</p>",
69
+ "<p>This is a safe description.&#60;script&#62;alert(&#39;This is an unsafe description.&#39;)&#60;/script&#62;</p>",
70
70
  );
71
71
  });
72
72
 
@@ -81,7 +81,7 @@ test("renders nested html calls", () => {
81
81
  // prettier-ignore
82
82
  assert.strictEqual(
83
83
  html`<p>!${conditionTrue ? html`<strong>${descriptionUnsafe}</strong>` : ""}</p>`,
84
- "<p><strong>&lt;script&gt;alert(&apos;This is an unsafe description.&apos;)&lt;/script&gt;</strong></p>",
84
+ "<p><strong>&#60;script&#62;alert(&#39;This is an unsafe description.&#39;)&#60;/script&#62;</strong></p>",
85
85
  );
86
86
  });
87
87
 
@@ -156,7 +156,7 @@ test("htmlGenerator renders unsafe content", () => {
156
156
 
157
157
  assert.strictEqual(
158
158
  accumulator,
159
- "<p>This is a safe description.&lt;script&gt;alert(&apos;This is an unsafe description.&apos;)&lt;/script&gt;12345255</p>",
159
+ "<p>This is a safe description.&#60;script&#62;alert(&#39;This is an unsafe description.&#39;)&#60;/script&#62;12345255</p>",
160
160
  );
161
161
  });
162
162
 
@@ -199,7 +199,7 @@ test("htmlGenerator works with other generators (escaped)", () => {
199
199
 
200
200
  assert.strictEqual(
201
201
  accumulator,
202
- "<div>&lt;p&gt;This is a safe description.&lt;script&gt;alert(&apos;This is an unsafe description.&apos;)&lt;/script&gt;12345255&lt;/p&gt;</div>",
202
+ "<div>&#60;p&#62;This is a safe description.&#60;script&#62;alert(&#39;This is an unsafe description.&#39;)&#60;/script&#62;12345255&#60;/p&#62;</div>",
203
203
  );
204
204
  assert.strictEqual(generator.next().done, true);
205
205
  });
@@ -229,7 +229,7 @@ test("htmlGenerator works with other generators within an array (escaped)", () =
229
229
 
230
230
  assert.strictEqual(
231
231
  accumulator,
232
- "<div>&lt;p&gt;This is a safe description.&lt;script&gt;alert(&apos;This is an unsafe description.&apos;)&lt;/script&gt;1,2,3,4,5255&lt;/p&gt;</div>",
232
+ "<div>&#60;p&#62;This is a safe description.&#60;script&#62;alert(&#39;This is an unsafe description.&#39;)&#60;/script&#62;1,2,3,4,5255&#60;/p&#62;</div>",
233
233
  );
234
234
  assert.strictEqual(generator.next().done, true);
235
235
  });
@@ -258,7 +258,7 @@ test("htmlAsyncGenerator renders unsafe content", async () => {
258
258
 
259
259
  assert.strictEqual(
260
260
  accumulator,
261
- "<p>This is a safe description.&lt;script&gt;alert(&apos;This is an unsafe description.&apos;)&lt;/script&gt;12345255</p>",
261
+ "<p>This is a safe description.&#60;script&#62;alert(&#39;This is an unsafe description.&#39;)&#60;/script&#62;12345255</p>",
262
262
  );
263
263
  });
264
264
 
@@ -286,7 +286,7 @@ test("htmlAsyncGenerator works with other generators (escaped)", async () => {
286
286
 
287
287
  assert.strictEqual(
288
288
  accumulator,
289
- "<div>&lt;p&gt;This is a safe description.&lt;script&gt;alert(&apos;This is an unsafe description.&apos;)&lt;/script&gt;12345255&lt;/p&gt;</div>",
289
+ "<div>&#60;p&#62;This is a safe description.&#60;script&#62;alert(&#39;This is an unsafe description.&#39;)&#60;/script&#62;12345255&#60;/p&#62;</div>",
290
290
  );
291
291
  });
292
292
 
@@ -302,7 +302,7 @@ test("htmlAsyncGenerator works with nested htmlAsyncGenerator calls in an array"
302
302
 
303
303
  assert.strictEqual(
304
304
  accumulator.replaceAll("\n", "").trim(),
305
- "1: <p># test.md&gt;</p>2: <p># test.md&gt;</p>3: <p># test.md&gt;</p>",
305
+ "1: <p># test.md&#62;</p>2: <p># test.md&#62;</p>3: <p># test.md&#62;</p>",
306
306
  );
307
307
  });
308
308
 
@@ -312,7 +312,7 @@ test("htmlAsyncGenerator renders chunks with promises (escaped)", async () => {
312
312
  })}</ul>`;
313
313
  const fileContent = readFileSync("test/test.md", "utf8").replaceAll(
314
314
  ">",
315
- "&gt;",
315
+ "&#62;",
316
316
  );
317
317
 
318
318
  let value = await generator.next();
@@ -372,7 +372,7 @@ test("htmlAsyncGenerator redners in chuncks", async () => {
372
372
  assert.strictEqual(value.value, "<ul>");
373
373
 
374
374
  value = await generator.next();
375
- assert.strictEqual(value.value, "&lt;p&gt;");
375
+ assert.strictEqual(value.value, "&#60;p&#62;");
376
376
 
377
377
  value = await generator.next();
378
378
  assert.strictEqual(value.value, "12");