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(toolbar): Tighten up iframe security and fix bug when saving cookie #79885

Closed
wants to merge 2 commits into from
Closed
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
83 changes: 51 additions & 32 deletions src/sentry/templates/sentry/toolbar/iframe.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@
}
}

let loginFormSetup = false;
function setupLoginForm() {
log('setupLoginForm()');
if (loginFormSetup) return;
const form = document.getElementById('login-form');
form.addEventListener('submit', submitEvent => {
submitEvent.preventDefault();
Expand All @@ -46,14 +48,7 @@
'popup=true,innerWidth=800,innerHeight=550,noopener=false'
);
});
}

function sendStateMessage(state) {
log('sendStateMessage(state)', { state });
window.parent.postMessage({
source: 'sentry-toolbar',
message: state
}, referrer);
loginFormSetup = true;
}

function listenForLoginSuccess() {
Expand All @@ -71,24 +66,49 @@
}

function saveAccessToken(data) {
log('saveAccessToken', data)
log('saveAccessToken', data);
let tokenValue;
let tokenType;

if (data.cookie) {
const value = `${data.cookie}; domain=${window.location.hostname}; path=/; max-age=31536000; SameSite=none; partitioned; secure`;
document.cookie = value
tokenValue = data.cookie;
tokenType = "cookie";
document.cookie = `${tokenValue}; domain=${window.location.hostname}; path=/; max-age=31536000; SameSite=none; partitioned; secure`;
log('Saved a cookie', document.cookie.indexOf(cookie) >= 0, cookie);
localStorage.setItem('cookie', data.cookie);
} else if (data.token) {
tokenValue = data.token;
tokenType = "accessToken";
}
if (data.token) {
localStorage.setItem('accessToken', data.token);
log('Saved an accessToken to localStorage', data.token);
else {
log('Unexpected: No access token or cookie found!');
return;
}
if (!data.cookie && !data.token) {
log('Unexpected: No access token found!');

try {
localStorage.setItem(tokenType, tokenValue);
log(`Saved {tokenType} to localStorage`, tokenValue);
} catch (err) {
log("Failed to use local storage:", err);
}
}

function setupMessageChannel() {
log('setupMessageChannel()');
log('state:', { state });

if (state !== 'success') {
// enum of: logged-out, missing-project, invalid-domain
window.parent.postMessage({
source: 'sentry-toolbar',
message: state
}, referrer);
setupLoginForm();
listenForLoginSuccess();
return;
}

// Never, ever setup up the channel unless state is "success"

const { port1, port2 } = new MessageChannel();

const messageDispatch = {
Expand Down Expand Up @@ -118,23 +138,29 @@
};
},
};
// This is critical as when using the `in` operator, one can access
// built-in special functions such as `__defineGetter__` etc and then
// inject their own code/functions or break out.
const validFunctions = new Set(Object.keys(messageDispatch));

port1.addEventListener('message', messageEvent => {
log('port.onMessage', messageEvent.data);

const { $id, message } = messageEvent.data;
if (!$id) {
if (!$id || !message) {
return; // MessageEvent is malformed, missing $id
}

if (!message.$function || !(message.$function in messageDispatch)) {
return; // No-op without a $function to call
const {$function, $args} = message;
if (!$function || !validFunctions.has($function)) {
return; // No-op without a $function to call or invalid function name
}

messageDispatch[message.$function]
.apply(undefined, message.$args || [])
.then($result => port1.postMessage({ $id, $result }))
.catch(error => port1.postMessage({ $id, $error: error }));
messageDispatch[$function](...($args || []))
.then(
$result => port1.postMessage({ $id, $result }),
$error => port1.postMessage({ $id, $error })
);
});
port1.start();

Expand All @@ -148,14 +174,7 @@

log('Init', { referrer, state });

if (state === 'success') {
setupMessageChannel();
} else {
setupLoginForm();
// enum of: logged-out, missing-project, invalid-domain
sendStateMessage(state);
listenForLoginSuccess();
}
setupMessageChannel();
})();
</script>
{% endscript %}
Expand Down
Loading