Skip to content

Commit

Permalink
fix: Same-origin iframe set/get cookie/localStorage bug (#600)
Browse files Browse the repository at this point in the history
* Set isSameOrigin after iframe src set

* same/cross origin iframe cookie/localStorage tests

---------

Co-authored-by: Sarah-Jane Skeete <[email protected]>
  • Loading branch information
SarahJane87 and Sarah-Jane Skeete authored Jun 12, 2024
1 parent b2f642b commit b292a14
Show file tree
Hide file tree
Showing 7 changed files with 288 additions and 9 deletions.
1 change: 1 addition & 0 deletions src/lib/web-worker/worker-iframe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export const patchHTMLIFrameElement = (WorkerHTMLIFrameElement: any, env: WebWor

env.$location$.href = src = resolveUrl(env, src, 'iframe');
env.$isLoading$ = 1;
env.$isSameOrigin$ = webWorkerCtx.$origin$ === env.$location$.origin;

setInstanceStateValue(this, StateProp.loadErrorStatus, undefined);

Expand Down
13 changes: 6 additions & 7 deletions src/lib/web-worker/worker-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,52 +6,51 @@ import { warnCrossOrigin } from '../log';
export const addStorageApi = (
win: any,
storageName: 'localStorage' | 'sessionStorage',
isSameOrigin: boolean,
env: WebWorkerEnvironment
) => {
let storage: Storage = {
getItem(key) {
if (isSameOrigin) {
if (env.$isSameOrigin$) {
return callMethod(win, [storageName, 'getItem'], [key], CallType.Blocking);
} else {
warnCrossOrigin('get', storageName, env);
}
},

setItem(key, value) {
if (isSameOrigin) {
if (env.$isSameOrigin$) {
callMethod(win, [storageName, 'setItem'], [key, value], CallType.Blocking);
} else {
warnCrossOrigin('set', storageName, env);
}
},

removeItem(key) {
if (isSameOrigin) {
if (env.$isSameOrigin$) {
callMethod(win, [storageName, 'removeItem'], [key], CallType.Blocking);
} else {
warnCrossOrigin('remove', storageName, env);
}
},

key(index) {
if (isSameOrigin) {
if (env.$isSameOrigin$) {
return callMethod(win, [storageName, 'key'], [index], CallType.Blocking);
} else {
warnCrossOrigin('key', storageName, env);
}
},

clear() {
if (isSameOrigin) {
if (env.$isSameOrigin$) {
callMethod(win, [storageName, 'clear'], EMPTY_ARRAY, CallType.Blocking);
} else {
warnCrossOrigin('clear', storageName, env);
}
},

get length() {
if (isSameOrigin) {
if (env.$isSameOrigin$) {
return getter(win, [storageName, 'length']);
} else {
warnCrossOrigin('length', storageName, env);
Expand Down
4 changes: 2 additions & 2 deletions src/lib/web-worker/worker-window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -417,8 +417,8 @@ export const createWindow = (
win.cancelIdleCallback = (id: number) => clearTimeout(id);

// add storage APIs to the window
addStorageApi(win, 'localStorage', $isSameOrigin$, env);
addStorageApi(win, 'sessionStorage', $isSameOrigin$, env);
addStorageApi(win, 'localStorage', env);
addStorageApi(win, 'sessionStorage', env);

if (!$isSameOrigin$) {
win.indexeddb = undefined;
Expand Down
25 changes: 25 additions & 0 deletions tests/platform/iframe/cookie.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<html>
<head>
<style>
body {
background-color: skyblue;
margin: 2px;
}
</style>
</head>
<body>
<div>
<span>iframe origin:</span>
<span id="iframe-origin"></span>
</div>
<div>
<span>cookie value:</span>
<span id="test-cookie"></span>
</div>
<script>
document.getElementById('iframe-origin').textContent = window.origin;
document.cookie = 'foo=88';
document.getElementById('test-cookie').textContent = document.cookie;
</script>
</body>
</html>
45 changes: 45 additions & 0 deletions tests/platform/iframe/iframe-cookie-localstorage.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ConsoleMessage, expect, test } from '@playwright/test';

test('iframe cookie & localStorage', async ({ page }) => {
let pageConsoleWarnings: Array<ConsoleMessage> = [];
page.on('console', msg => {
if (msg.type() === 'warning') {
pageConsoleWarnings.push(msg);
}
});

await page.goto('/tests/platform/iframe/index-cookie-localstorage.html');
await page.waitForSelector('.completed');

pageConsoleWarnings = [];
const sameOriginCookieButton = page.locator('#sameOriginCookieButton');
await sameOriginCookieButton.click();
const sameOriginCookie = page.frameLocator('#iframe-same-origin-cookie').locator('#test-cookie');
await expect(sameOriginCookie).toContainText('foo=88');
expect(pageConsoleWarnings.length).toBe(0);

pageConsoleWarnings = [];
const sameOriginLocalStorageButton = page.locator('#sameOriginLocalStorageButton');
await sameOriginLocalStorageButton.click();
const sameOriginLocalStorage = page.frameLocator('#iframe-same-origin-localstorage').locator('#test-localstorage');
await expect(sameOriginLocalStorage).toContainText('88');
expect(pageConsoleWarnings.length).toBe(0);

pageConsoleWarnings = [];
const crossOriginCookieButton = page.locator('#crossOriginCookieButton');
await crossOriginCookieButton.click();
const crossOriginCookie = page.frameLocator('#iframe-cross-origin-cookie').locator('#test-cookie');
await expect(crossOriginCookie).not.toContainText('foo=88');
expect(pageConsoleWarnings.length).toBe(2);
expect(pageConsoleWarnings.some(warning => warning.text().includes('Partytown unable to set cross-origin cookie'))).toBe(true);
expect(pageConsoleWarnings.some(warning => warning.text().includes('Partytown unable to get cross-origin cookie'))).toBe(true);

pageConsoleWarnings = [];
const crossOriginLocalStorageButton = page.locator('#crossOriginLocalStorageButton');
await crossOriginLocalStorageButton.click();
const crossOriginLocalStorage = page.frameLocator('#iframe-cross-origin-localstorage').locator('#test-localstorage');
await expect(crossOriginLocalStorage).not.toContainText('88');
expect(pageConsoleWarnings.length).toBe(2);
expect(pageConsoleWarnings.some(warning => warning.text().includes('Partytown unable to set cross-origin localStorage'))).toBe(true);
expect(pageConsoleWarnings.some(warning => warning.text().includes('Partytown unable to get cross-origin localStorage'))).toBe(true);
});
184 changes: 184 additions & 0 deletions tests/platform/iframe/index-cookie-localstorage.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="Partytown Test Page" />
<title>Iframe origin</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif,
Apple Color Emoji, Segoe UI Emoji;
font-size: 12px;
}

h1 {
margin: 0 0 15px 0;
}

ul {
list-style-type: none;
margin: 0;
padding: 0;
}

a {
display: block;
padding: 16px 8px;
}

a:link,
a:visited {
text-decoration: none;
color: blue;
}

a:hover {
background-color: #eee;
}

li {
display: block;
height: 80px;
}

li strong,
li code,
li button {
white-space: nowrap;
margin: 0 5px;
min-width: 10px;
}

iframe {
width: 100%;
height: 36px;
border: none;
}
</style>
<script>
window.name = 'Main';
</script>
<script>
partytown = {
logCalls: true,
logGetters: true,
logSetters: true,
logImageRequests: true,
logSendBeaconRequests: true,
logStackTraces: false,
logScriptExecution: true,
};
</script>
<script src="/~partytown/debug/partytown.js"></script>
</head>

<body>
<h1>Iframe cookie & localStorage</h1>
<ul>
<li>
<strong>Same origin cookie set/get</strong>
<button id="sameOriginCookieButton">Create iframe</button>
<code id="testSameOriginCookie"></code>
<script type="text/partytown">
(function () {
const btn = document.getElementById('sameOriginCookieButton');
btn.addEventListener('click', function (ev) {
const elm = document.getElementById('testSameOriginCookie');
const iframe = document.createElement('iframe');
iframe.id = 'iframe-same-origin-cookie';
iframe.style.height = '60px';
const url = new URL('/tests/platform/iframe/cookie.html', location.origin);
iframe.src = url.href;
elm.parentNode.appendChild(iframe);
});
})();
</script>
</li>

<li>
<strong>Same origin localStorage set/get</strong>
<button id="sameOriginLocalStorageButton">Create iframe</button>
<code id="testSameOriginLocalStorage"></code>
<script type="text/partytown">
(function () {
const btn = document.getElementById('sameOriginLocalStorageButton');
btn.addEventListener('click', function (ev) {
const elm = document.getElementById('testSameOriginLocalStorage');
const iframe = document.createElement('iframe');
iframe.id = 'iframe-same-origin-localstorage';
iframe.style.height = '60px';
const url = new URL('/tests/platform/iframe/localstorage.html', location.origin);
iframe.src = url.href;
elm.parentNode.appendChild(iframe);
});
})();
</script>
</li>

<li>
<strong>Cross origin cookie set/get</strong>
<button id="crossOriginCookieButton">Create iframe</button>
<code id="testCrossOriginCookie"></code>
<script type="text/partytown">
(function () {
const btn = document.getElementById('crossOriginCookieButton');
btn.addEventListener('click', function (ev) {
const url = new URL('/tests/platform/iframe/cookie.html', location.origin);
fetch(url)
.then(response => response.text())
.then(htmlContent => {
// simulating cross origin iframe by using a datauri
// which won't have the same origin
const dataUri = `data:text/html;base64,${btoa(htmlContent)}`;
const elm = document.getElementById('testCrossOriginCookie');
const iframe = document.createElement('iframe');
iframe.id = 'iframe-cross-origin-cookie';
iframe.style.height = '60px';
iframe.src = dataUri;
elm.parentNode.appendChild(iframe);
});
});
})();
</script>
</li>

<li>
<strong>Cross origin localStorage set/get</strong>
<button id="crossOriginLocalStorageButton">Create iframe</button>
<code id="testCrossOriginLocalStorage"></code>
<script type="text/partytown">
(function () {
const btn = document.getElementById('crossOriginLocalStorageButton');
btn.addEventListener('click', function (ev) {
const url = new URL('/tests/platform/iframe/localstorage.html', location.origin);
fetch(url)
.then(response => response.text())
.then(htmlContent => {
// simulating cross origin iframe by using a datauri
// which won't have the same origin
const dataUri = `data:text/html;base64,${btoa(htmlContent)}`;
const elm = document.getElementById('testCrossOriginLocalStorage');
const iframe = document.createElement('iframe');
iframe.id = 'iframe-cross-origin-localstorage';
iframe.style.height = '60px';
iframe.src = dataUri;
elm.parentNode.appendChild(iframe);
});
});
})();
</script>
</li>
</ul>

<script type="text/partytown">
(function () {
document.body.classList.add('completed');
})();
</script>
<hr />
<p><a href="/tests/">All Tests</a></p>
</body>

</html>
25 changes: 25 additions & 0 deletions tests/platform/iframe/localstorage.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<html>
<head>
<style>
body {
background-color: skyblue;
margin: 2px;
}
</style>
</head>
<body>
<div>
<span>iframe origin:</span>
<span id="iframe-origin"></span>
</div>
<div>
<span>localStorage value:</span>
<span id="test-localstorage"></span>
</div>
<script>
document.getElementById('iframe-origin').textContent = window.origin;
window.localStorage.setItem('bar', '88');
document.getElementById('test-localstorage').textContent = window.localStorage.getItem('bar');
</script>
</body>
</html>

0 comments on commit b292a14

Please sign in to comment.