Skip to content

Commit df1ea50

Browse files
authored
Merge pull request from GHSA-vvhj-v88f-5gxr
* add = to escaped chars * add Security to README and remove XSS protection * add ` * remove = * format * improve readme.md * update * update README.md
1 parent b8e5889 commit df1ea50

File tree

4 files changed

+28
-22
lines changed

4 files changed

+28
-22
lines changed

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ npm i ghtml
1212

1313
### `html`
1414

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 `!`.
1616

1717
### `htmlGenerator`
1818

@@ -32,7 +32,7 @@ Because they return generators instead of strings, a key difference of `htmlGene
3232

3333
### `includeFile`
3434

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.
3636

3737
## Usage
3838

@@ -41,11 +41,11 @@ Available for Node.js users, the `includeFile` function is a wrapper around `rea
4141
```js
4242
import { html } from "ghtml";
4343

44-
const username = '<img src="https://example.com/hacker.png">';
44+
const username = '<img src="https://example.com/pwned.png">';
4545
const greeting = html`<h1>Hello, ${username}!</h1>`;
4646

4747
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>
4949
```
5050

5151
To bypass escaping:
@@ -168,3 +168,7 @@ const logo = includeFile("static/logo.svg");
168168
console.log(logo);
169169
// Output: content of "static/logo.svg"
170170
```
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.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"description": "Replace your template engine with fast JavaScript by leveraging the power of tagged templates.",
44
"author": "Gürgün Dayıoğlu",
55
"license": "MIT",
6-
"version": "1.7.2",
6+
"version": "2.0.0",
77
"type": "module",
88
"main": "./src/index.js",
99
"exports": {
@@ -22,7 +22,7 @@
2222
"devDependencies": {
2323
"@fastify/pre-commit": "^2.1.0",
2424
"c8": "^9.1.0",
25-
"grules": "^0.17.1",
25+
"grules": "^0.17.2",
2626
"tinybench": "^2.8.0"
2727
},
2828
"repository": {

src/html.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
const escapeDictionary = {
2-
'"': "&quot;",
3-
"'": "&apos;",
4-
"&": "&amp;",
5-
"<": "&lt;",
6-
">": "&gt;",
2+
'"': "&#34;",
3+
"&": "&#38;",
4+
"'": "&#39;",
5+
"<": "&#60;",
6+
">": "&#62;",
7+
"`": "&#96;",
78
};
89

910
const escapeRegExp = new RegExp(
@@ -19,6 +20,7 @@ const escapeFunction = (string) => {
1920

2021
do {
2122
const escapedCharacter = escapeDictionary[string[end++]];
23+
2224
if (escapedCharacter) {
2325
escaped += string.slice(start, end - 1) + escapedCharacter;
2426
start = end;

test/index.js

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,14 @@ test("renders safe content", () => {
5959
test("renders unsafe content", () => {
6060
assert.strictEqual(
6161
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>`,
6363
);
6464
});
6565

6666
test("renders arrays", () => {
6767
assert.strictEqual(
6868
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>",
7070
);
7171
});
7272

@@ -81,7 +81,7 @@ test("renders nested html calls", () => {
8181
// prettier-ignore
8282
assert.strictEqual(
8383
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>",
8585
);
8686
});
8787

@@ -156,7 +156,7 @@ test("htmlGenerator renders unsafe content", () => {
156156

157157
assert.strictEqual(
158158
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>",
160160
);
161161
});
162162

@@ -199,7 +199,7 @@ test("htmlGenerator works with other generators (escaped)", () => {
199199

200200
assert.strictEqual(
201201
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>",
203203
);
204204
assert.strictEqual(generator.next().done, true);
205205
});
@@ -229,7 +229,7 @@ test("htmlGenerator works with other generators within an array (escaped)", () =
229229

230230
assert.strictEqual(
231231
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>",
233233
);
234234
assert.strictEqual(generator.next().done, true);
235235
});
@@ -258,7 +258,7 @@ test("htmlAsyncGenerator renders unsafe content", async () => {
258258

259259
assert.strictEqual(
260260
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>",
262262
);
263263
});
264264

@@ -286,7 +286,7 @@ test("htmlAsyncGenerator works with other generators (escaped)", async () => {
286286

287287
assert.strictEqual(
288288
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>",
290290
);
291291
});
292292

@@ -302,7 +302,7 @@ test("htmlAsyncGenerator works with nested htmlAsyncGenerator calls in an array"
302302

303303
assert.strictEqual(
304304
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>",
306306
);
307307
});
308308

@@ -312,7 +312,7 @@ test("htmlAsyncGenerator renders chunks with promises (escaped)", async () => {
312312
})}</ul>`;
313313
const fileContent = readFileSync("test/test.md", "utf8").replaceAll(
314314
">",
315-
"&gt;",
315+
"&#62;",
316316
);
317317

318318
let value = await generator.next();
@@ -372,7 +372,7 @@ test("htmlAsyncGenerator redners in chuncks", async () => {
372372
assert.strictEqual(value.value, "<ul>");
373373

374374
value = await generator.next();
375-
assert.strictEqual(value.value, "&lt;p&gt;");
375+
assert.strictEqual(value.value, "&#60;p&#62;");
376376

377377
value = await generator.next();
378378
assert.strictEqual(value.value, "12");

0 commit comments

Comments
 (0)