Skip to content

Commit

Permalink
Add AMP loop protection (#182)
Browse files Browse the repository at this point in the history
  • Loading branch information
SlayterDev authored Feb 13, 2024
1 parent 581c6a2 commit c1beb76
Show file tree
Hide file tree
Showing 6 changed files with 237 additions and 0 deletions.
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ <h2>Privacy Protections Tests</h2>
<li><a href='./privacy-protections/surrogates/'>Surrogates</a></li>
<li><a href='./privacy-protections/gpc/'>Global Privacy Control</a></li>
<li><a href='./privacy-protections/amp/'>AMP Links</a></li>
<li><a href='./privacy-protections/amp-loop-protection/'>AMP Loop Protection</a></li>
<li><a href='./privacy-protections/query-parameters/'>Query Parameters</a></li>
<li><a href='./content-scope-scripts/runtime-checks/'>Runtime checks</a></li>
</ul>
Expand Down
58 changes: 58 additions & 0 deletions privacy-protections/amp-loop-protection/amp-only.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AMP loop protection</title>
</head>
<body>
<h1 id="param-display"></h1>
<script>
const MAX = 15;
const LS_ITEM_KEY = 'amp-loop-protection-attempt';
const newUrl = new URL(location.href);
const isStart = newUrl.searchParams.has('start');

if (isStart) {
localStorage[LS_ITEM_KEY] = 0;
}
const attempt = Number.parseInt(localStorage[LS_ITEM_KEY], 10) || 0;
localStorage[LS_ITEM_KEY] = attempt + 1;

document.getElementById('param-display').innerText = `${newUrl.searchParams}`;
document.body.innerHTML += `<p>Attempt ${attempt + 1}/${MAX}</p>`;

// set amp attr, count attempts
const params = new URLSearchParams(location.search);
if (params.get('amp') === '1') {
document.documentElement.setAttribute('amp', '');
const link = document.createElement('link');
link.setAttribute('rel', 'canonical');
const ampUrl = new URL('http://good.third-party.site/privacy-protections/amp-loop-protection/amp-only.html');
if (isStart) {
ampUrl.searchParams.set('start', '1');
}
link.setAttribute('href', ampUrl.href);
document.head.appendChild(link);

localStorage.removeItem(LS_ITEM_KEY);
const onMessage = msg => {
if (msg.data.action && msg.data.action === 'url') {
if (window.opener) {
window.opener.postMessage({ url: document.location.href, type: msg.data.type }, '*');
} else if (window.parent) {
window.parent.postMessage({ url: document.location.href, type: msg.data.type }, '*');
}
}
};

window.addEventListener('message', onMessage);
} else if (attempt < MAX) {
newUrl.searchParams.set('amp', 1);
newUrl.pathname = newUrl.pathname
location.href = newUrl.href;
}
</script>
</body>
</html>
28 changes: 28 additions & 0 deletions privacy-protections/amp-loop-protection/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AMP loop protection</title>

<script src='./main.js' defer></script>
<link href='./style.css' rel='stylesheet'></link>
</head>
<body>
<p><a href="../../">[Home]</a><a href="../">[Privacy Protections Tests]</a><strong>[AMP Upgrade Loop Protection]</strong></p>

<p>This test will navigate to a non-AMP page that immediately redirects to its AMP version. This will cause a non-AMP↔AMP loop (client trying to get real page, page redirecting). Clients should detect this scenario and allow the AMP page to load.</p>
<p>If the loop protection works the reported url will have the amp=1 parameter.</p>

<p><button id='start'>Start test</button></p>

<details id='tests' hidden>
<summary id='tests-summary'></summary>
<ul id='tests-details'>
</ul>
</details>

<p><button id='download' disabled>Download the result</button></p>

</body>
</html>
142 changes: 142 additions & 0 deletions privacy-protections/amp-loop-protection/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
const startButton = document.querySelector('#start');
const downloadButton = document.querySelector('#download');

const testsDiv = document.querySelector('#tests');
const testsSummaryDiv = document.querySelector('#tests-summary');
const testsDetailsDiv = document.querySelector('#tests-details');

const TEST_DOMAIN = 'good.third-party.site';

const tests = [
{
id: 'rewrite-amp',
run: () => {
let res;
const promise = new Promise((resolve, reject) => { res = resolve; });
const otherWindow = window.open(`http://${TEST_DOMAIN}/privacy-protections/amp-loop-protection/amp-only.html?amp=1&start`);

const interval = setInterval(() => {
otherWindow.postMessage({ action: 'url', type: 'navigation' }, `http://${TEST_DOMAIN}/`);
}, 500);

function onMessage (m) {
if (m.data && m.data.type === 'navigation') {
clearInterval(interval);
otherWindow.close();
window.removeEventListener('message', onMessage);
console.log('navigation', m.data.url);
res(m.data.url);
}
}

window.addEventListener('message', onMessage);

return promise;
}
}
];

// object that contains results of all tests
const results = {
page: 'amp-loop-protection',
date: null,
results: []
};

function resultToHTML (data) {
if (Array.isArray(data)) {
return `<ul>${data.map(r => `<li>${r.test} - ${r.result}</li>`).join('')}</ul>`;
} else if (data) {
return JSON.stringify(data, null, 2);
}

return null;
}

/**
* Test runner
*/
function runTests () {
startButton.setAttribute('disabled', 'disabled');
downloadButton.removeAttribute('disabled');
testsDiv.removeAttribute('hidden');

results.results.length = 0;
results.date = (new Date()).toUTCString();
let all = 0;
let failed = 0;

testsDetailsDiv.innerHTML = '';

function updateSummary () {
testsSummaryDiv.innerText = `Performed ${all} tests${failed > 0 ? ` (${failed} failed)` : ''}. Click for details.`;
}

for (const test of tests) {
const resultObj = {
id: test.id,
value: null
};
results.results.push(resultObj);

const li = document.createElement('li');
li.id = `test-${test.id.replace(' ', '-')}`;
li.innerHTML = `${test.id} - <span class='value'>…</span>`;
const valueSpan = li.querySelector('.value');

testsDetailsDiv.appendChild(li);

try {
const result = test.run();

if (result instanceof Promise) {
result
.then(data => {
valueSpan.textContent = resultToHTML(data);
resultObj.value = data || null;
})
.catch(e => {
failed++;
valueSpan.innerHTML = `❌ error thrown ("${e.message ? e.message : e}")`;
updateSummary();
});
} else {
valueSpan.innerHTML = resultToHTML(result);
resultObj.value = result || null;
}
} catch (e) {
failed++;
valueSpan.innerHTML = `❌ error thrown ("${e.message ? e.message : e}")`;
}

all++;
}

updateSummary();

startButton.removeAttribute('disabled');
}

function downloadTheResults () {
const data = JSON.stringify(results, null, 2);
const a = document.createElement('a');
const url = window.URL.createObjectURL(new Blob([data], { type: 'application/json' }));
a.href = url;
a.download = 'amp-loop-protection-results.json';

document.body.appendChild(a);
a.click();

window.URL.revokeObjectURL(url);
a.remove();
}

downloadButton.addEventListener('click', () => downloadTheResults());

// run tests if button was clicked or…
startButton.addEventListener('click', () => runTests());

// if url query is '?run' start tests imadiatelly
if (document.location.search === '?run') {
runTests();
}
7 changes: 7 additions & 0 deletions privacy-protections/amp-loop-protection/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
* {
box-sizing: border-box;
}

.value {
color: gray;
}
1 change: 1 addition & 0 deletions privacy-protections/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ <h1>Privacy Protections Tests</h1>
<li><a href='./surrogates/'>Surrogates</a></li>
<li><a href='./gpc/'>Global Privacy Control</a></li>
<li><a href='./amp/'>AMP Links</a></li>
<li><a href='./amp-loop-protection/'>AMP Loop Protection</a></li>
<li><a href='./query-parameters/'>Query Parameters</a></li>
</ul>

Expand Down

0 comments on commit c1beb76

Please sign in to comment.