Open FossPrime opened 3 years ago
Do we currently send X-Frame options? I don't think we send them, or do you mean something different?
https://codesandbox.io/s/builtin-browser-bug-y02x9?file=/src/index.ts
codesandbox.io doesn't... but popular API's often do... like Google/Microsoft/Facebook/LinkedIn's OAuth and SAML API's... as well as Captchas, Credit Card verification, Government ID verification, Online Notarization, and Affiliate link programs. Anything that requires good federation with a sensitive federation that goes as follows:
This is something webstorm and VSCode do natively, but we can't do on CodeSandbox.io even if we have the PWA installed. We get elevated privileges to background sync and keyboard shortcuts when we install the PWA, but not the ability to write federated apps in codesandbox.
I've modified the OP with screenshots of part of the problem. I'm using an iFrame... which isn't what you do in Federation, but it is what CodeSandbox.io has to do in practice.
Here's the code for similar extensions including one by trailfire that does exactly what I'm proposing... except it whitelists the TrialFire app.
https://chrome.google.com/webstore/detail/trialfire-iframe-enabler/bgcohdnafofkgkaelmkelamidkdgbchh
// listen.js
'use strict';
(function() {
/**
* Listen for Trialfire WebExt messages.
*/
window.addEventListener('message', message => {
if (message.data && (message.data.topic === 'tfWebExt')) {
let command = message.data.data;
switch (command.method) {
case 'injectMe':
commandInjectMe(command);
break;
}
}
});
/**
* Inject and initialize the tracking-module snippet.
*/
function commandInjectMe(data) {
console.log('web-ext is injecting trialfire');
let script = document.querySelector('[data-tf-api-token]');
if (script) {
console.log('trialfire is already in scope');
} else {
// Annotate the body with the API token.
// Tracking-module will automatically initialize with this API token.
document.body.setAttribute('data-tf-api-token', data.apiToken);
// Inject the tracking-module snippet.
script = document.createElement('script');
script.src = data.assetSrc;
document.body.appendChild(script);
}
}
})();
// trialfire.JS
'use strict';
const config = {
// Headers we want to strip from the response.
xssHeaders: new Set([
'content-security-policy',
'x-content-security-options',
'x-frame-options',
'x-webkit-csp',
'x-xss-protection'
])
};
const state = {
// The set of tabs IDs that have loaded Trialfire domains.
tabs: new Set()
};
/**
* Record the IDs of tabs that load a Trialfire domain.
*/
chrome.tabs.onUpdated.addListener(onTabUpdated);
function onTabUpdated(tabId, changeInfo, tab) {
if (tab && tab.url) {
// Determine if this tab has loaded a Trialfire domain.
let inTrialfire = !!~tab.url.toLowerCase()
.indexOf('.trialfire.com');
// Add or remove this tab from the set of Trialfire tabs.
let knownTab = state.tabs.has(tabId);
if (!knownTab && inTrialfire) {
// Tab has loaded a Trialfire domain.
state.tabs.add(tabId);
} else
if (knownTab && !inTrialfire) {
// Tab has unloaded a Trialfire domain.
state.tabs.delete(tabId);
}
}
}
/**
* Strip XSS headers from tabs that have loaded Trialfire domains.
*/
chrome.webRequest.onHeadersReceived.addListener(stripXSSHeaders, {
urls: ['<all_urls>']
},
// Needs to be blocking so headers can be modified.
['blocking', 'responseHeaders', 'extraHeaders']);
function stripXSSHeaders(details) {
let headers;
if (state.tabs.has(details.tabId)) {
headers = details.responseHeaders.filter(header => !config.xssHeaders.has(header.name.toLowerCase()));
} else {
headers = details.responseHeaders;
}
return {
responseHeaders: headers
};
}
/**
* Listen for DOMContentLoaded events in Trialfire tabs.
*/
chrome.webNavigation.onDOMContentLoaded.addListener(onDOMContentLoaded);
function onDOMContentLoaded(details) {
// Frame ID must be non-zero to indicate nested iframe.
if (state.tabs.has(details.tabId) && (details.frameId > 0)) {
if (details.url.toLowerCase()
.startsWith('http')) {
chrome.tabs.executeScript(details.tabId, {
frameId: details.frameId,
file: '/listen.js',
runAt: 'document_start'
});
}
}
}
iFrame Allow
chrome.webRequest.onHeadersReceived.addListener(
function(details) {
for (var j = 0; j < details.responseHeaders.length; ++j) {
if (details.responseHeaders[j].name.toLowerCase() == 'x-frame-options') {
details.responseHeaders.splice(j, 1);
return {
responseHeaders: details.responseHeaders
};
}
}
}, {
urls: ["<all_urls>"]
}, ["blocking", "responseHeaders"]);
🌈 Feature
Please make the browser preview behave more like a full browser by removing X-Frame options, which blocks working on Oauth/SAML and other federated features.
https://codesandbox.io/s/builtin-browser-bug-y02x9?file=/src/index.ts
Before Removing X-Frame-options:
There is an extension that already does it here: https://chrome.google.com/webstore/detail/iframe-allow/gifgpciglhhpmeefjdmlpboipkibhbjg
But its security is bit lax. The CSB extension should only do it within the embedded browser preview or only on our Sandboxes.
For linkedIn and youtube you'll might also have to remove the CSP frameblock.
Refused to frame 'https://accounts.youtube.com/' because an ancestor violates the following Content Security Policy directive: "frame-ancestors https://accounts.google.com".
After you install an Allow iFrame extension that removes X-Frame-options... which is what I'm proposing the CSB browser extension to do for iFrame document requests made inside the Sandbox browser: