-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move back privacy test pages to reduce duplication (#329)
* Move back privacy test pages to reduce duplication * Fix up ts and add CODEOWNERS
- Loading branch information
1 parent
6d145db
commit 9eb4010
Showing
13 changed files
with
508 additions
and
224 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
/** | ||
* Tests for runtime checks | ||
*/ | ||
import { processConfig } from '../src/utils.js' | ||
import { setup } from './helpers/harness.js' | ||
import * as fs from 'fs' | ||
|
||
describe('Test integration pages', () => { | ||
let browser | ||
let server | ||
let teardown | ||
let setupIntegrationPagesServer | ||
let gotoAndWait | ||
beforeAll(async () => { | ||
({ browser, setupIntegrationPagesServer, teardown, gotoAndWait } = await setup({ withExtension: true })) | ||
server = setupIntegrationPagesServer() | ||
}) | ||
afterAll(async () => { | ||
await server?.close() | ||
await teardown() | ||
}) | ||
|
||
it('Script that should not execute', async () => { | ||
const pages = { | ||
'runtime-checks/pages/basic-run.html': 'runtime-checks/config/basic-run.json', | ||
'runtime-checks/pages/filter-props.html': 'runtime-checks/config/filter-props.json' | ||
} | ||
for (const pageName in pages) { | ||
const configName = pages[pageName] | ||
|
||
const port = server.address().port | ||
const page = await browser.newPage() | ||
const res = fs.readFileSync(process.cwd() + '/integration-test/test-pages/' + configName) | ||
// @ts-expect-error - JSON.parse returns any | ||
const config = JSON.parse(res) | ||
// Pollyfill for globalThis methods needed in processConfig | ||
globalThis.document = { | ||
referrer: 'http://localhost:8080', | ||
location: { | ||
href: 'http://localhost:8080', | ||
// @ts-expect-error - ancestorOrigins is not defined in the type definition | ||
ancestorOrigins: { | ||
length: 0 | ||
} | ||
} | ||
} | ||
globalThis.location = { | ||
href: 'http://localhost:8080', | ||
// @ts-expect-error - ancestorOrigins is not defined in the type definition | ||
ancestorOrigins: { | ||
length: 0 | ||
} | ||
} | ||
|
||
const processedConfig = processConfig(config, /* userList */ [], /* preferences */ {}/*, platformSpecificFeatures = [] */) | ||
|
||
await gotoAndWait(page, `http://localhost:${port}/${pageName}?automation=true`, processedConfig) | ||
// Check page results | ||
const pageResults = await page.evaluate( | ||
async () => { | ||
let res | ||
const promise = new Promise(resolve => { | ||
res = resolve | ||
}) | ||
// @ts-expect-error - results is not defined in the type definition | ||
if (window.results) { | ||
// @ts-expect-error - results is not defined in the type definition | ||
res(window.results) | ||
} else { | ||
window.addEventListener('results-ready', (e) => { | ||
// @ts-expect-error - e.detail is not defined in the type definition | ||
res(e.detail) | ||
}) | ||
} | ||
return promise | ||
} | ||
) | ||
for (const key in pageResults) { | ||
for (const result of pageResults[key]) { | ||
expect(result.result).withContext(key + ':\n ' + result.name).toEqual(result.expected) | ||
} | ||
} | ||
} | ||
}) | ||
}) |
19 changes: 19 additions & 0 deletions
19
integration-test/test-pages/runtime-checks/config/basic-run.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{ | ||
"features": { | ||
"runtimeChecks": { | ||
"state": "enabled", | ||
"exceptions": [], | ||
"settings": { | ||
"taintCheck": "enabled", | ||
"matchAllDomains": "enabled", | ||
"matchAllStackDomains": "enabled", | ||
"overloadInstanceOf": "enabled", | ||
"elementRemovalTimeout": 1000, | ||
"domains": [ | ||
], | ||
"stackDomains": [ | ||
] | ||
} | ||
} | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
integration-test/test-pages/runtime-checks/config/filter-props.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"features": { | ||
"runtimeChecks": { | ||
"state": "enabled", | ||
"exceptions": [], | ||
"settings": { | ||
"taintCheck": "enabled", | ||
"matchAllDomains": "enabled", | ||
"matchAllStackDomains": "enabled", | ||
"overloadInstanceOf": "enabled", | ||
"domains": [ | ||
], | ||
"stackDomains": [ | ||
], | ||
"tagModifiers": { | ||
"script": { | ||
"filters": { | ||
"property": ["madeUpProp1", "madeUpProp3"], | ||
"attribute": ["madeupattr1", "madeupattr3"] | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta charset="utf-8"> | ||
<meta name="viewport" content="width=device-width"> | ||
<title>Runtime checks</title> | ||
</head> | ||
<body> | ||
<p><a href="../../index.html">[Home]</a></p> | ||
|
||
<p>Runtime element interrogation (runtimeChecks) allows our clients the ability to validate, inspect and modify elements as they get injected into a web page by website scripts.</p> | ||
<ul> | ||
<li><a href="./pages/basic-run.html">Basic Run</a> - <a href="./config/basic-run.json">Config</a></li> | ||
<li><a href="./pages/filter-props.html">Filter props</a> - <a href="./config/filter-props.json">Config</a></li> | ||
</ul> | ||
|
||
</body> | ||
</html> |
119 changes: 119 additions & 0 deletions
119
integration-test/test-pages/runtime-checks/pages/basic-run.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta charset="utf-8"> | ||
<meta name="viewport" content="width=device-width"> | ||
<title>Runtime checks</title> | ||
<link rel="stylesheet" href="../../shared/style.css"> | ||
</head> | ||
<body> | ||
<script src="../../shared/utils.js"></script> | ||
<p><a href="../index.html">[Runtime checks]</a></p> | ||
|
||
<p>This page verifies that runtime checking is enabled given the corresponding <a href="../config/basic-run.json">config</a></p> | ||
|
||
<script> | ||
// eslint-disable-next-line no-undef | ||
test('Script that should not execute', async () => { | ||
window.scripty1Ran = false; | ||
const scriptElement = document.createElement('script'); | ||
scriptElement.innerText = 'window.scripty1Ran = true'; | ||
scriptElement.id = 'scripty'; | ||
scriptElement.setAttribute('type', 'application/evilscript'); | ||
document.body.appendChild(scriptElement); | ||
const hadInspectorNode = scriptElement === document.querySelector('ddg-runtime-checks:last-of-type'); | ||
// Continue to modify the script element after it has been added to the DOM | ||
scriptElement.integrity = 'sha256-123'; | ||
scriptElement.madeUpProp = 'val'; | ||
const instanceofResult = scriptElement instanceof HTMLScriptElement; | ||
const scripty = document.querySelector('#scripty'); | ||
|
||
return [ | ||
{ name: 'hadInspectorNode', result: hadInspectorNode, expected: true }, | ||
{ name: 'expect script to match', result: scripty, expected: scriptElement }, | ||
{ name: 'instanceof matches HTMLScriptElement', result: instanceofResult, expected: true }, | ||
{ name: 'scripty.integrity', result: scripty.integrity, expected: 'sha256-123' }, | ||
{ name: 'scripty.madeUpProp', result: scripty.madeUpProp, expected: 'val' }, | ||
{ name: 'scripty.type', result: scripty.type, expected: 'application/evilscript' }, | ||
{ name: 'scripty.id', result: scripty.id, expected: 'scripty' }, | ||
{ name: 'script ran', result: window.scripty1Ran, expected: false } | ||
]; | ||
}); | ||
|
||
// eslint-disable-next-line no-undef | ||
test('Script that should execute', async () => { | ||
window.scripty2Ran = false; | ||
const scriptElement = document.createElement('script'); | ||
scriptElement.innerText = 'window.scripty2Ran = true'; | ||
scriptElement.id = 'scripty2'; | ||
scriptElement.setAttribute('type', 'application/javascript'); | ||
document.body.appendChild(scriptElement); | ||
const hadInspectorNode = scriptElement === document.querySelector('ddg-runtime-checks:last-of-type'); | ||
// Continue to modify the script element after it has been added to the DOM | ||
scriptElement.madeUpProp = 'val'; | ||
const instanceofResult = scriptElement instanceof HTMLScriptElement; | ||
const scripty = document.querySelector('#scripty2'); | ||
|
||
return [ | ||
{ name: 'hadInspectorNode', result: hadInspectorNode, expected: true }, | ||
{ name: 'expect script to match', result: scripty, expected: scriptElement }, | ||
{ name: 'instanceof matches HTMLScriptElement', result: instanceofResult, expected: true }, | ||
{ name: 'scripty.madeUpProp', result: scripty.madeUpProp, expected: 'val' }, | ||
{ name: 'scripty.type', result: scripty.type, expected: 'application/javascript' }, | ||
{ name: 'scripty.id', result: scripty.id, expected: 'scripty2' }, | ||
{ name: 'script ran', result: window.scripty2Ran, expected: true } | ||
]; | ||
}); | ||
|
||
// eslint-disable-next-line no-undef | ||
test('Invalid external script should trigger error listeners', async () => { | ||
const scriptElement = document.createElement('script'); | ||
scriptElement.id = 'scripty3'; | ||
scriptElement.src = 'invalid://url'; | ||
scriptElement.setAttribute('type', 'application/javascript'); | ||
|
||
let listenerCount = 0; | ||
let resolver = null; | ||
const promise = new Promise(resolve => { | ||
resolver = resolve; | ||
}); | ||
scriptElement.onerror = () => { | ||
listenerCount++; | ||
resolver(); | ||
}; | ||
|
||
let resolver2 = null; | ||
const promise2 = new Promise(resolve => { | ||
resolver2 = resolve; | ||
}); | ||
scriptElement.addEventListener('error', () => { | ||
listenerCount++; | ||
resolver2(); | ||
}); | ||
|
||
document.body.appendChild(scriptElement); | ||
await Promise.all([promise, promise2]); | ||
|
||
const hadInspectorNode = scriptElement === document.querySelector('ddg-runtime-checks:last-of-type'); | ||
// Continue to modify the script element after it has been added to the DOM | ||
// @ts-expect-error https://app.asana.com/0/1201614831475344/1203979574128023/f | ||
scriptElement.madeUpProp = 'val'; | ||
const instanceofResult = scriptElement instanceof HTMLScriptElement; | ||
const scripty = document.querySelector('#scripty3'); | ||
|
||
return [ | ||
{ name: 'listenerCount', result: listenerCount, expected: 2 }, | ||
{ name: 'hadInspectorNode', result: hadInspectorNode, expected: true }, | ||
{ name: 'instanceof matches HTMLScriptElement', result: instanceofResult, expected: true }, | ||
{ name: 'scripty.madeUpProp', result: scripty.madeUpProp, expected: 'val' }, | ||
{ name: 'scripty.type', result: scripty.type, expected: 'application/javascript' }, | ||
{ name: 'scripty.id', result: scripty.id, expected: 'scripty3' }, | ||
{ name: 'scripty.src', result: scripty.src, expected: 'invalid://url' } | ||
]; | ||
}); | ||
|
||
// eslint-disable-next-line no-undef | ||
renderResults(); | ||
</script> | ||
</body> | ||
</html> |
Oops, something went wrong.