Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Same-origin iframe set/get cookie/localStorage bug #600

Merged
merged 2 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>
Loading