| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 663c580 commit c1b5154
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -299,8 +299,10 @@ function createLoaderScript(srcList: SrcScriptTag[], enableTrustedTypes = false) | |||
| 299 | 299 | .map((s) => { | |
| 300 | 300 | // URI encoding means value can't escape string, JS, or HTML context. | |
| 301 | 301 | const srcAttr = encodeURI(s.src).replaceAll("'", "\\'"); | |
| 302 | - // Can only be 'module' or a JS MIME type or an empty string. | ||
| 303 | - const typeAttr = s.type ? "'" + s.type + "'" : "''"; | ||
| 302 | + // 'module', a JS MIME type, or an empty string. A JS MIME type may carry | ||
| 303 | + // parameters after a ';', which isJavascriptMimeType() does not constrain, | ||
| 304 | + // so encode this the same way as integrity and crossOrigin below. | ||
| 305 | + const typeAttr = JSON.stringify(s.type ?? '').replaceAll('<', '\\u003c'); | ||
| 304 | 306 | const asyncAttr = !!s.async; | |
| 305 | 307 | const deferAttr = !!s.defer; | |
| 306 | 308 | const integrityAttr = JSON.stringify(s.integrity ?? null).replaceAll('<', '\\u003c'); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -58,7 +58,7 @@ describe('auto-csp', () => { | |||
| 58 | 58 | const csps = getCsps(result); | |
| 59 | 59 | expect(csps).toHaveSize(1); | |
| 60 | 60 | expect(csps[0]).toMatch(CSP_SINGLE_HASH_REGEX); | |
| 61 | - expect(result).toContain(`const scripts = [['./main.js', '', false, false, null, null]];`); | ||
| 61 | + expect(result).toContain(`const scripts = [['./main.js', "", false, false, null, null]];`); | ||
| 62 | 62 | }); | |
| 63 | 63 | ||
| 64 | 64 | it('should rewrite a single source script in place', async () => { | |
@@ -78,7 +78,7 @@ describe('auto-csp', () => { | |||
| 78 | 78 | expect(csps[0]).toMatch(CSP_SINGLE_HASH_REGEX); | |
| 79 | 79 | // Our loader script appears after the HTML text content. | |
| 80 | 80 | expect(result).toMatch( | |
| 81 | - /Some text<\/div>\s*<script>\(\(\) => {\s*const scripts = \[\['.\/main.js', '', false, false, null, null\]\];/, | ||
| 81 | + /Some text<\/div>\s*<script>\(\(\) => {\s*const scripts = \[\['.\/main.js', "", false, false, null, null\]\];/, | ||
| 82 | 82 | ); | |
| 83 | 83 | }); | |
| 84 | 84 | ||
@@ -103,7 +103,7 @@ describe('auto-csp', () => { | |||
| 103 | 103 | expect(csps[0]).toMatch(CSP_TWO_HASHES_REGEX); | |
| 104 | 104 | expect(result).toContain( | |
| 105 | 105 | // eslint-disable-next-line max-len | |
| 106 | - `const scripts = [['./main1.js', '', false, false, null, null],['./main2.js', '', true, false, null, null],['./main3.js', 'module', true, true, null, null]];`, | ||
| 106 | + `const scripts = [['./main1.js', "", false, false, null, null],['./main2.js', "", true, false, null, null],['./main3.js', "module", true, true, null, null]];`, | ||
| 107 | 107 | ); | |
| 108 | 108 | // Head loader script is in the head. | |
| 109 | 109 | expect(result).toContain(`</script></head>`); | |
@@ -166,12 +166,12 @@ describe('auto-csp', () => { | |||
| 166 | 166 | // Loader script for main.js and main2.js appear after 'foo' and before 'bar'. | |
| 167 | 167 | expect(result).toMatch( | |
| 168 | 168 | // eslint-disable-next-line max-len | |
| 169 | - /console.log\('foo'\);<\/script>\s*<script>\(\(\) => {\s*const scripts = \[\['.\/main.js', '', false, false, null, null\],\['.\/main2.js', '', false, false, null, null\]\];[\s\S]*console.log\('bar'\);/, | ||
| 169 | + /console.log\('foo'\);<\/script>\s*<script>\(\(\) => {\s*const scripts = \[\['.\/main.js', "", false, false, null, null\],\['.\/main2.js', "", false, false, null, null\]\];[\s\S]*console.log\('bar'\);/, | ||
| 170 | 170 | ); | |
| 171 | 171 | // Loader script for main3.js and main4.js appear after 'bar'. | |
| 172 | 172 | expect(result).toMatch( | |
| 173 | 173 | // eslint-disable-next-line max-len | |
| 174 | - /console.log\('bar'\);<\/script>\s*<script>\(\(\) => {\s*const scripts = \[\['.\/main3.js', '', false, false, null, null\],\['.\/main4.js', '', false, false, null, null\]\];/, | ||
| 174 | + /console.log\('bar'\);<\/script>\s*<script>\(\(\) => {\s*const scripts = \[\['.\/main3.js', "", false, false, null, null\],\['.\/main4.js', "", false, false, null, null\]\];/, | ||
| 175 | 175 | ); | |
| 176 | 176 | // Exactly 4 scripts should be left. | |
| 177 | 177 | expect(Array.from(result.matchAll(/<script>/gi)).length).toEqual(4); | |
@@ -238,7 +238,7 @@ describe('auto-csp', () => { | |||
| 238 | 238 | expect(csps).toHaveSize(1); | |
| 239 | 239 | expect(csps[0]).toMatch(CSP_SINGLE_HASH_REGEX); | |
| 240 | 240 | expect(result).toContain( | |
| 241 | - `const scripts = [['./main.js', 'module', false, false, "sha384-xyz123", "anonymous"]];`, | ||
| 241 | + `const scripts = [['./main.js', "module", false, false, "sha384-xyz123", "anonymous"]];`, | ||
| 242 | 242 | ); | |
| 243 | 243 | }); | |
| 244 | 244 | ||
@@ -258,7 +258,7 @@ describe('auto-csp', () => { | |||
| 258 | 258 | expect(csps).toHaveSize(1); | |
| 259 | 259 | expect(csps[0]).toMatch(CSP_SINGLE_HASH_REGEX); | |
| 260 | 260 | expect(result).toContain( | |
| 261 | - `const scripts = [['./main.js', '', false, false, "sha384-xyz123", null]];`, | ||
| 261 | + `const scripts = [['./main.js', "", false, false, "sha384-xyz123", null]];`, | ||
| 262 | 262 | ); | |
| 263 | 263 | }); | |
| 264 | 264 | ||
@@ -278,7 +278,7 @@ describe('auto-csp', () => { | |||
| 278 | 278 | expect(csps).toHaveSize(1); | |
| 279 | 279 | expect(csps[0]).toMatch(CSP_SINGLE_HASH_REGEX); | |
| 280 | 280 | expect(result).toContain( | |
| 281 | - `const scripts = [['./main.js', '', false, false, null, "anonymous"]];`, | ||
| 281 | + `const scripts = [['./main.js', "", false, false, null, "anonymous"]];`, | ||
| 282 | 282 | ); | |
| 283 | 283 | }); | |
| 284 | 284 | ||
@@ -296,7 +296,7 @@ describe('auto-csp', () => { | |||
| 296 | 296 | expect(csps).toHaveSize(1); | |
| 297 | 297 | expect(csps[0]).toMatch(CSP_SINGLE_HASH_REGEX); | |
| 298 | 298 | expect(result).toContain( | |
| 299 | - `const scripts = [['./main.js', 'application/javascript', false, false, null, null]];`, | ||
| 299 | + `const scripts = [['./main.js', "application/javascript", false, false, null, null]];`, | ||
| 300 | 300 | ); | |
| 301 | 301 | }); | |
| 302 | 302 | ||
@@ -314,7 +314,7 @@ describe('auto-csp', () => { | |||
| 314 | 314 | expect(csps).toHaveSize(1); | |
| 315 | 315 | expect(csps[0]).toMatch(CSP_SINGLE_HASH_REGEX); | |
| 316 | 316 | expect(result).toContain( | |
| 317 | - `const scripts = [['./main.js', 'Text/JavaScript ; charset=utf-8', false, false, null, null]];`, | ||
| 317 | + `const scripts = [['./main.js', "Text/JavaScript ; charset=utf-8", false, false, null, null]];`, | ||
| 318 | 318 | ); | |
| 319 | 319 | }); | |
| 320 | 320 | ||
@@ -332,7 +332,7 @@ describe('auto-csp', () => { | |||
| 332 | 332 | expect(csps).toHaveSize(1); | |
| 333 | 333 | expect(csps[0]).toMatch(CSP_SINGLE_HASH_REGEX); | |
| 334 | 334 | expect(result).toContain( | |
| 335 | - `const scripts = [['./main.js', 'Module', false, false, null, null]];`, | ||
| 335 | + `const scripts = [['./main.js', "Module", false, false, null, null]];`, | ||
| 336 | 336 | ); | |
| 337 | 337 | }); | |
| 338 | 338 | ||
@@ -351,6 +351,44 @@ describe('auto-csp', () => { | |||
| 351 | 351 | expect(result).not.toContain('const scripts ='); | |
| 352 | 352 | }); | |
| 353 | 353 | ||
| 354 | + it('should encode a script type that carries MIME parameters', async () => { | ||
| 355 | + const result = await autoCsp(` | ||
| 356 | + <html> | ||
| 357 | + <head> | ||
| 358 | + </head> | ||
| 359 | + <body> | ||
| 360 | + <script src="./main.js" type="text/javascript;']];var x=1;var junk=[['a','b"></script> | ||
| 361 | + </body> | ||
| 362 | + </html> | ||
| 363 | + `); | ||
| 364 | + | ||
| 365 | + const csps = getCsps(result); | ||
| 366 | + expect(csps).toHaveSize(1); | ||
| 367 | + expect(csps[0]).toMatch(CSP_SINGLE_HASH_REGEX); | ||
| 368 | + // The type stays inside its string literal. | ||
| 369 | + expect(result).toContain( | ||
| 370 | + `const scripts = [['./main.js', "text/javascript;']];var x=1;var junk=[['a','b", false, false, null, null]];`, | ||
| 371 | + ); | ||
| 372 | + }); | ||
| 373 | + | ||
| 374 | + it('should encode a script type that contains a closing script tag', async () => { | ||
| 375 | + const result = await autoCsp(` | ||
| 376 | + <html> | ||
| 377 | + <head> | ||
| 378 | + </head> | ||
| 379 | + <body> | ||
| 380 | + <script src="./main.js" type="text/javascript;</script><script>x</script>"></script> | ||
| 381 | + </body> | ||
| 382 | + </html> | ||
| 383 | + `); | ||
| 384 | + | ||
| 385 | + const csps = getCsps(result); | ||
| 386 | + expect(csps).toHaveSize(1); | ||
| 387 | + expect(csps[0]).toMatch(CSP_SINGLE_HASH_REGEX); | ||
| 388 | + // Only the loader element is emitted. | ||
| 389 | + expect(Array.from(result.matchAll(/<script/gi)).length).toEqual(1); | ||
| 390 | + }); | ||
| 391 | + | ||
| 354 | 392 | describe('isJavascriptMimeType', () => { | |
| 355 | 393 | it('should identify standard JavaScript MIME types', () => { | |
| 356 | 394 | expect(isJavascriptMimeType('text/javascript')).toBeTrue(); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments