fix(sdk): Ignore messages from a different origin and sanitize URLs (#8879)

This PR includes some specific security related fixes

1. Validate the origin of any message events
2. Sanitize URLs before opening them

---------

Co-authored-by: Pranav <pranav@chatwoot.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This commit is contained in:
Shivam Mishra
2025-08-14 14:33:32 +05:30
committed by GitHub
parent c6be04cdc1
commit a42b99ada0

View File

@@ -50,11 +50,35 @@ const updateCampaignReadStatus = baseDomain => {
});
};
const sanitizeURL = url => {
if (url === '') return '';
try {
// any invalid url will not be accepted
// example - JaVaScRiP%0at:alert(document.domain)"
// this has an obfuscated javascript protocol
const parsedURL = new URL(url);
// filter out dangerous protocols like `javascript`, `data`, `vbscript`
if (!['https', 'http'].includes(parsedURL.protocol)) {
throw new Error('Invalid Protocol');
}
} catch (e) {
// eslint-disable-next-line no-console
console.error('Invalid URL', e);
}
return 'about:blank'; // blank page URL
};
export const IFrameHelper = {
getUrl({ baseUrl, websiteToken }) {
baseUrl = sanitizeURL(baseUrl);
return `${baseUrl}/widget?website_token=${websiteToken}`;
},
createFrame: ({ baseUrl, websiteToken }) => {
baseUrl = sanitizeURL(baseUrl);
if (IFrameHelper.getAppFrame()) {
return;
}
@@ -102,10 +126,12 @@ export const IFrameHelper = {
window.onmessage = e => {
if (
typeof e.data !== 'string' ||
e.data.indexOf('chatwoot-widget:') !== 0
e.data.indexOf('chatwoot-widget:') !== 0 ||
e.origin !== window.location.origin
) {
return;
}
const message = JSON.parse(e.data.replace('chatwoot-widget:', ''));
if (typeof IFrameHelper.events[message.event] === 'function') {
IFrameHelper.events[message.event](message);
@@ -140,7 +166,9 @@ export const IFrameHelper = {
},
setupAudioListeners: () => {
const { baseUrl = '' } = window.$chatwoot;
let { baseUrl = '' } = window.$chatwoot;
baseUrl = sanitizeURL(baseUrl);
getAlertAudio(baseUrl, { type: 'widget', alertTone: 'ding' }).then(() =>
initOnEvents.forEach(event => {
document.removeEventListener(
@@ -234,6 +262,7 @@ export const IFrameHelper = {
},
popoutChatWindow: ({ baseUrl, websiteToken, locale }) => {
baseUrl = sanitizeURL(baseUrl);
const cwCookie = Cookies.get('cw_conversation');
window.$chatwoot.toggle('close');
popoutChatWindow(baseUrl, websiteToken, locale, cwCookie);