From 02ed9f5e663b5646fb4ec576abd12380fff1b5a7 Mon Sep 17 00:00:00 2001
From: Benoit Socias
Date: Thu, 22 Sep 2022 12:55:18 +0000
Subject: [PATCH] [FIX] website, *: avoid using unescaped query parameters
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
*: google_recaptcha, web_editor, website_blog, website_event_booth,
website_event_exhibitor, website_event_track, website_forum,
website_jitsi, website_sale_comparison, website_slides,
website_livechat, website_twitter
This commit fixes improperly escaped query parameters across javascripts
of website-related modules.
See https://github.com/odoo/enterprise/pull/31711
closes odoo/odoo#118599
X-original-commit: 89be076aa51b097ada48d0046006be4d9692ac1e
Related: odoo/enterprise#39783
Signed-off-by: Bojabza Soukéina (sobo)
---
addons/google_recaptcha/static/src/js/recaptcha.js | 2 +-
addons/web_editor/static/shapes/convert.js | 4 ++--
.../src/components/media_dialog/document_selector.js | 4 ++--
.../src/components/media_dialog/file_selector.js | 2 +-
.../src/components/media_dialog/image_selector.js | 2 +-
.../src/components/media_dialog/video_selector.js | 4 ++--
.../web_editor/static/src/js/backend/convert_inline.js | 2 +-
.../static/src/js/editor/odoo-editor/src/OdooEditor.js | 2 +-
.../web_editor/static/src/js/editor/snippets.editor.js | 2 +-
.../static/src/js/editor/snippets.options.js | 10 +++++-----
.../src/js/wysiwyg/widgets/link_popover_widget.js | 2 +-
addons/web_editor/static/src/js/wysiwyg/wysiwyg.js | 6 +++---
.../src/client_actions/configurator/configurator.js | 4 ++--
.../client_actions/website_preview/website_preview.js | 4 ++--
addons/website/static/src/components/dialog/seo.js | 2 +-
.../src/components/wysiwyg_adapter/wysiwyg_adapter.js | 2 +-
addons/website/static/src/js/backend/dashboard.js | 2 +-
addons/website/static/src/js/content/website_root.js | 4 ++--
addons/website/static/src/js/editor/snippets.editor.js | 2 +-
.../website/static/src/js/editor/snippets.options.js | 8 +++++---
addons/website/static/src/services/website_service.js | 2 +-
.../static/src/snippets/s_social_media/options.js | 8 ++++----
.../static/src/snippets/s_website_form/options.js | 2 +-
.../static/src/systray_items/website_switcher.js | 2 +-
addons/website_blog/static/src/js/website_blog.js | 10 +++++-----
.../static/src/js/booth_register.js | 2 +-
.../static/src/js/event_exhibitor_connect.js | 2 +-
.../static/src/js/website_event_track_proposal_form.js | 2 +-
.../src/js/systray_items/forum_forum_add_form.js | 2 +-
addons/website_forum/static/src/js/website_forum.js | 4 ++--
addons/website_jitsi/static/src/js/chat_room.js | 2 +-
.../static/src/core/persona_model_patch.js | 2 +-
.../static/src/core/thread_service_patch.js | 2 +-
.../static/src/js/website_sale_comparison.js | 7 ++++---
.../static/src/js/slides_course_fullscreen_player.js | 2 +-
.../website_slides/static/src/js/slides_course_join.js | 4 ++--
.../website_slides/static/src/js/slides_course_quiz.js | 2 +-
.../website_slides/static/src/js/slides_slide_like.js | 2 +-
.../static/src/js/website.twitter.animation.js | 2 +-
39 files changed, 67 insertions(+), 64 deletions(-)
diff --git a/addons/google_recaptcha/static/src/js/recaptcha.js b/addons/google_recaptcha/static/src/js/recaptcha.js
index 7d5153215c5..6ac81a13322 100644
--- a/addons/google_recaptcha/static/src/js/recaptcha.js
+++ b/addons/google_recaptcha/static/src/js/recaptcha.js
@@ -21,7 +21,7 @@ const ReCaptcha = Class.extend({
*/
loadLibs: function () {
if (this._publicKey) {
- this._recaptchaReady = loadJS(`https://www.recaptcha.net/recaptcha/api.js?render=${this._publicKey}`)
+ this._recaptchaReady = loadJS(`https://www.recaptcha.net/recaptcha/api.js?render=${encodeURIComponent(this._publicKey)}`)
.then(() => new Promise(resolve => window.grecaptcha.ready(() => resolve())));
return this._recaptchaReady.then(() => !!document.querySelector('.grecaptcha-badge'));
}
diff --git a/addons/web_editor/static/shapes/convert.js b/addons/web_editor/static/shapes/convert.js
index 2688f4aa86d..7a556a5f28d 100644
--- a/addons/web_editor/static/shapes/convert.js
+++ b/addons/web_editor/static/shapes/convert.js
@@ -48,14 +48,14 @@ files.filter(f => f.endsWith('svg')).forEach(filePath => {
repeatX: fileName.includes('repeatx'),
repeatY: fileName.includes('repeaty'),
};
- shape.optionXML = ``;
+ shape.optionXML = ``;
if (shape.position[0] === 'stretch') {
shape.position = ['center'];
shape.size = '100% 100%';
} else {
shape.size = '100% auto';
}
- shape.scss = `'${shape.page}/${shape.name}': ('position': ${shape.position[0]}, 'size': ${shape.size}, 'colors': (${shape.colors.join(', ')})${shape.repeatX ? ", 'repeat-x': true" : ""}${shape.repeatY ? ", 'repeat-y': true" : ""})`;
+ shape.scss = `'${encodeURIComponent(shape.page)}/${encodeURIComponent(shape.name)}': ('position': ${shape.position[0]}, 'size': ${shape.size}, 'colors': (${shape.colors.join(', ')})${shape.repeatX ? ", 'repeat-x': true" : ""}${shape.repeatY ? ", 'repeat-y': true" : ""})`;
shapes.push(shape);
});
const xml = shapes.map(shape => shape.optionXML).join('\n');
diff --git a/addons/web_editor/static/src/components/media_dialog/document_selector.js b/addons/web_editor/static/src/components/media_dialog/document_selector.js
index a8c84d61ec0..707e6baf3d7 100644
--- a/addons/web_editor/static/src/components/media_dialog/document_selector.js
+++ b/addons/web_editor/static/src/components/media_dialog/document_selector.js
@@ -49,7 +49,7 @@ export class DocumentSelector extends FileSelector {
static async createElements(selectedMedia, { orm }) {
return Promise.all(selectedMedia.map(async attachment => {
const linkEl = document.createElement('a');
- let href = `/web/content/${attachment.id}?unique=${attachment.checksum}&download=true`;
+ let href = `/web/content/${encodeURIComponent(attachment.id)}?unique=${encodeURIComponent(attachment.checksum)}&download=true`;
if (!attachment.public) {
let accessToken = attachment.access_token;
if (!accessToken) {
@@ -59,7 +59,7 @@ export class DocumentSelector extends FileSelector {
[attachment.id],
);
}
- href += `&access_token=${accessToken}`;
+ href += `&access_token=${encodeURIComponent(accessToken)}`;
}
linkEl.href = href;
linkEl.title = attachment.name;
diff --git a/addons/web_editor/static/src/components/media_dialog/file_selector.js b/addons/web_editor/static/src/components/media_dialog/file_selector.js
index e13fb71a8b8..3fec0d99b14 100644
--- a/addons/web_editor/static/src/components/media_dialog/file_selector.js
+++ b/addons/web_editor/static/src/components/media_dialog/file_selector.js
@@ -40,7 +40,7 @@ AttachmentError.template = xml `
following pages or views:
-
-
+
diff --git a/addons/web_editor/static/src/components/media_dialog/image_selector.js b/addons/web_editor/static/src/components/media_dialog/image_selector.js
index 4726a4ab802..0cf76da6438 100644
--- a/addons/web_editor/static/src/components/media_dialog/image_selector.js
+++ b/addons/web_editor/static/src/components/media_dialog/image_selector.js
@@ -272,7 +272,7 @@ export class ImageSelector extends FileSelector {
[attachment.id],
);
}
- src += `?access_token=${accessToken}`;
+ src += `?access_token=${encodeURIComponent(accessToken)}`;
}
imageEl.src = src;
imageEl.alt = attachment.description || '';
diff --git a/addons/web_editor/static/src/components/media_dialog/video_selector.js b/addons/web_editor/static/src/components/media_dialog/video_selector.js
index 8704f8d9ac1..e6088583b79 100644
--- a/addons/web_editor/static/src/components/media_dialog/video_selector.js
+++ b/addons/web_editor/static/src/components/media_dialog/video_selector.js
@@ -89,11 +89,11 @@ export class VideoSelector extends Component {
onMounted(async () => {
await Promise.all(this.props.vimeoPreviewIds.map(async (videoId) => {
- const { thumbnail_url: thumbnailSrc } = await this.http.get(`https://vimeo.com/api/oembed.json?url=http%3A//vimeo.com/${videoId}`);
+ const { thumbnail_url: thumbnailSrc } = await this.http.get(`https://vimeo.com/api/oembed.json?url=http%3A//vimeo.com/${encodeURIComponent(videoId)}`);
this.state.vimeoPreviews.push({
id: videoId,
thumbnailSrc,
- src: `https://player.vimeo.com/video/${videoId}`
+ src: `https://player.vimeo.com/video/${encodeURIComponent(videoId)}`
});
}));
});
diff --git a/addons/web_editor/static/src/js/backend/convert_inline.js b/addons/web_editor/static/src/js/backend/convert_inline.js
index 8f602d793a2..2d11ead61c8 100644
--- a/addons/web_editor/static/src/js/backend/convert_inline.js
+++ b/addons/web_editor/static/src/js/backend/convert_inline.js
@@ -721,7 +721,7 @@ function fontToImg($editable) {
const image = document.createElement('img');
image.setAttribute('width', intrinsicWidth);
image.setAttribute('height', intrinsicHeight);
- image.setAttribute('src', `/web_editor/font_to_img/${content.charCodeAt(0)}/${window.encodeURI(color)}/${window.encodeURI(bg)}/${Math.max(1, Math.round(intrinsicWidth))}x${Math.max(1, Math.round(intrinsicHeight))}`);
+ image.setAttribute('src', `/web_editor/font_to_img/${content.charCodeAt(0)}/${encodeURIComponent(color)}/${encodeURIComponent(bg)}/${Math.max(1, Math.round(intrinsicWidth))}x${Math.max(1, Math.round(intrinsicHeight))}`);
image.setAttribute('data-class', font.getAttribute('class'));
image.setAttribute('data-style', style);
image.setAttribute('style', style);
diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js b/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js
index 8a7d6d04d2b..0cf80615eb4 100644
--- a/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js
+++ b/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js
@@ -4397,7 +4397,7 @@ export class OdooEditor extends EventTarget {
videoElement.setAttribute('height', '315');
videoElement.setAttribute(
'src',
- `https://www.youtube.com/embed/${youtubeUrl[1]}`,
+ `https://www.youtube.com/embed/${encodeURIComponent(youtubeUrl[1])}`,
);
videoElement.setAttribute('title', 'YouTube video player');
videoElement.setAttribute('frameborder', '0');
diff --git a/addons/web_editor/static/src/js/editor/snippets.editor.js b/addons/web_editor/static/src/js/editor/snippets.editor.js
index a7ee7bb6cc1..c80c5dd48cf 100644
--- a/addons/web_editor/static/src/js/editor/snippets.editor.js
+++ b/addons/web_editor/static/src/js/editor/snippets.editor.js
@@ -3751,7 +3751,7 @@ var SnippetsMenu = Widget.extend({
$content: $('', {text: sprintf(_t("Do you want to install the %s App?"), name)}).append(
$('', {
target: '_blank',
- href: '/web#id=' + moduleID + '&view_type=form&model=ir.module.module&action=base.open_module_tree',
+ href: '/web#id=' + encodeURIComponent(moduleID) + '&view_type=form&model=ir.module.module&action=base.open_module_tree',
text: _t("More info about this app."),
class: 'ml4',
})
diff --git a/addons/web_editor/static/src/js/editor/snippets.options.js b/addons/web_editor/static/src/js/editor/snippets.options.js
index 35af113e60b..7beae559547 100644
--- a/addons/web_editor/static/src/js/editor/snippets.options.js
+++ b/addons/web_editor/static/src/js/editor/snippets.options.js
@@ -6128,7 +6128,7 @@ registry.ImageTools = ImageHandlerOption.extend({
const [module, directory, fileName] = shapeName.split('/');
let shape = this.shapeCache[fileName];
if (!shape) {
- const shapeURL = `/${module}/static/image_shapes/${directory}/${fileName}.svg`;
+ const shapeURL = `/${encodeURIComponent(module)}/static/image_shapes/${encodeURIComponent(directory)}/${encodeURIComponent(fileName)}.svg`;
shape = await (await fetch(shapeURL)).text();
this.shapeCache[fileName] = shape;
}
@@ -6334,7 +6334,7 @@ registry.ImageTools = ImageHandlerOption.extend({
uiFragment.querySelectorAll('we-select-page we-button[data-set-img-shape]').forEach(btn => {
const image = document.createElement('img');
const [moduleName, directory, shapeName] = btn.dataset.setImgShape.split('/');
- image.src = `/${moduleName}/static/image_shapes/${directory}/${shapeName}.svg`;
+ image.src = `/${encodeURIComponent(moduleName)}/static/image_shapes/${encodeURIComponent(directory)}/${encodeURIComponent(shapeName)}.svg`;
$(btn).prepend(image);
if (btn.dataset.animated) {
@@ -6411,7 +6411,7 @@ registry.ImageTools = ImageHandlerOption.extend({
// attribute.
match = match.slice(0, -12);
}
- return this._loadImageInfo(`/web/image/${match}`);
+ return this._loadImageInfo(`/web/image/${encodeURIComponent(match)}`);
}
return _super(...arguments);
},
@@ -7163,9 +7163,9 @@ registry.BackgroundShape = SnippetOptionWidget.extend({
return `${colorName}=${encodedCol}`;
});
if (flip.length) {
- searchParams.push(`flip=${flip.sort().join('')}`);
+ searchParams.push(`flip=${encodeURIComponent(flip.sort().join(''))}`);
}
- return `/web_editor/shape/${shape}.svg?${searchParams.join('&')}`;
+ return `/web_editor/shape/${encodeURIComponent(shape)}.svg?${searchParams.join('&')}`;
},
/**
* Retrieves current shape data from the target's dataset.
diff --git a/addons/web_editor/static/src/js/wysiwyg/widgets/link_popover_widget.js b/addons/web_editor/static/src/js/wysiwyg/widgets/link_popover_widget.js
index 0de5cfb9d4b..424e3311025 100644
--- a/addons/web_editor/static/src/js/wysiwyg/widgets/link_popover_widget.js
+++ b/addons/web_editor/static/src/js/wysiwyg/widgets/link_popover_widget.js
@@ -209,7 +209,7 @@ const LinkPopoverWidget = Widget.extend({
// would need to fetch the page through the server (s2s), involving
// enduser fetching problematic pages such as illicit content.
this.$previewFaviconImg.attr({
- 'src': `https://www.google.com/s2/favicons?sz=16&domain=${url}`
+ 'src': `https://www.google.com/s2/favicons?sz=16&domain=${encodeURIComponent(url)}`
}).removeClass('d-none');
this.$previewFaviconFa.addClass('d-none');
} else {
diff --git a/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js b/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js
index 320d9738bce..0852d3e673c 100644
--- a/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js
+++ b/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js
@@ -219,7 +219,7 @@ const Wysiwyg = Widget.extend({
categories: powerboxOptions.categories,
plugins: options.editorPlugins,
direction: options.direction || localization.direction || 'ltr',
- collaborationClientAvatarUrl: `${browser.location.origin}/web/image?model=res.users&field=avatar_128&id=${this.getSession().uid}`,
+ collaborationClientAvatarUrl: `${browser.location.origin}/web/image?model=res.users&field=avatar_128&id=${encodeURIComponent(this.getSession().uid)}`,
renderingClasses: ['o_dirty', 'o_transform_removal', 'oe_edited_link', 'o_menu_loading'],
dropImageAsAttachment: options.dropImageAsAttachment,
foldSnippets: !!options.foldSnippets,
@@ -503,7 +503,7 @@ const Wysiwyg = Widget.extend({
}
return this._userName;
},
- get_client_avatar: () => `${browser.location.origin}/web/image?model=res.users&field=avatar_128&id=${this.getSession().uid}`,
+ get_client_avatar: () => `${browser.location.origin}/web/image?model=res.users&field=avatar_128&id=${encodeURIComponent(this.getSession().uid)}`,
get_missing_steps: (params) => this.odooEditor.historyGetMissingSteps(params.requestPayload),
get_history_from_snapshot: () => this.odooEditor.historyGetSnapshotSteps(),
get_collaborative_selection: () => this.odooEditor.getCurrentCollaborativeSelection(),
@@ -964,7 +964,7 @@ const Wysiwyg = Widget.extend({
// it was modified previously, as the other modified image may be used
// elsewhere if the snippet was duplicated or was saved as a custom one.
const newAttachmentSrc = await this._rpc({
- route: `/web_editor/modify_image/${el.dataset.originalId}`,
+ route: `/web_editor/modify_image/${encodeURIComponent(el.dataset.originalId)}`,
params: {
res_model: resModel,
res_id: parseInt(resId),
diff --git a/addons/website/static/src/client_actions/configurator/configurator.js b/addons/website/static/src/client_actions/configurator/configurator.js
index c62521e5aae..c7bb7df59f6 100644
--- a/addons/website/static/src/client_actions/configurator/configurator.js
+++ b/addons/website/static/src/client_actions/configurator/configurator.js
@@ -372,7 +372,7 @@ class ApplyConfiguratorScreen extends Component {
// Here the website service goToWebsite method is not used because
// the web client needs to be reloaded after the new modules have
// been installed.
- window.location.replace(`/web#action=website.website_preview&website_id=${resp.website_id}&enable_editor=1&with_loader=1`);
+ window.location.replace(`/web#action=website.website_preview&website_id=${encodeURIComponent(resp.website_id)}&enable_editor=1&with_loader=1`);
}
}
}
@@ -634,7 +634,7 @@ export class Configurator extends Component {
}
get pathname() {
- return `/website/configurator${this.state.currentStep ? `/${this.state.currentStep}` : ''}`;
+ return `/website/configurator${this.state.currentStep ? `/${encodeURIComponent(this.state.currentStep)}` : ''}`;
}
get storageItemName() {
diff --git a/addons/website/static/src/client_actions/website_preview/website_preview.js b/addons/website/static/src/client_actions/website_preview/website_preview.js
index da5c9631e3a..9e83357cedc 100644
--- a/addons/website/static/src/client_actions/website_preview/website_preview.js
+++ b/addons/website/static/src/client_actions/website_preview/website_preview.js
@@ -65,9 +65,9 @@ export class WebsitePreview extends Component {
// URL (event if it wasn't, it wouldn't be an issue as those are
// really considered as the same domain, the user will share the
// same session and CORS errors won't be a thing in such a case)
- window.location.href = `${this.websiteDomain}/web#action=website.website_preview&path=${encodedPath}&website_id=${this.websiteId}`;
+ window.location.href = `${encodeURI(this.websiteDomain)}/web#action=website.website_preview&path=${encodedPath}&website_id=${encodeURIComponent(this.websiteId)}`;
} else {
- this.initialUrl = `/website/force/${this.websiteId}?path=${encodedPath}`;
+ this.initialUrl = `/website/force/${encodeURIComponent(this.websiteId)}?path=${encodedPath}`;
}
});
diff --git a/addons/website/static/src/components/dialog/seo.js b/addons/website/static/src/components/dialog/seo.js
index a89b586dd04..da690aad728 100644
--- a/addons/website/static/src/components/dialog/seo.js
+++ b/addons/website/static/src/components/dialog/seo.js
@@ -29,7 +29,7 @@ class ImageSelector extends Component {
this.seoContext = useState(seoContext);
const firstImageId = this.props.hasSocialDefaultImage ? 'social_default_image' : 'logo';
- const firstImageSrc = `/web/image/website/${this.website.currentWebsite.id}/${firstImageId}`;
+ const firstImageSrc = `/web/image/website/${encodeURIComponent(this.website.currentWebsite.id)}/${firstImageId}`;
const firstImage = {
src: firstImageSrc,
active: this.areSameImages(firstImageSrc, this.seoContext.metaImage),
diff --git a/addons/website/static/src/components/wysiwyg_adapter/wysiwyg_adapter.js b/addons/website/static/src/components/wysiwyg_adapter/wysiwyg_adapter.js
index 2070b1ea9a6..02f4d30a51b 100644
--- a/addons/website/static/src/components/wysiwyg_adapter/wysiwyg_adapter.js
+++ b/addons/website/static/src/components/wysiwyg_adapter/wysiwyg_adapter.js
@@ -696,7 +696,7 @@ export class WysiwygAdapterComponent extends ComponentAdapter {
} else if (event.data.reloadWebClient) {
const currentPath = encodeURIComponent(window.location.pathname);
const websiteId = this.websiteService.currentWebsite.id;
- callback = () => window.location = `/web#action=website.website_preview&website_id=${websiteId}&path=${currentPath}&enable_editor=1`;
+ callback = () => window.location = `/web#action=website.website_preview&website_id=${encodeURIComponent(websiteId)}&path=${currentPath}&enable_editor=1`;
} else if (event.data.action) {
callback = () => {
this.leaveEditMode({
diff --git a/addons/website/static/src/js/backend/dashboard.js b/addons/website/static/src/js/backend/dashboard.js
index f55ef0ed248..ce1f42c870c 100644
--- a/addons/website/static/src/js/backend/dashboard.js
+++ b/addons/website/static/src/js/backend/dashboard.js
@@ -103,7 +103,7 @@ var Dashboard = AbstractAction.extend({
on_go_to_website: function (ev) {
ev.preventDefault();
var website = this.websites.find(website => website.selected);
- window.location.replace(`/web#action=website.website_preview&website_id=${website.id}`);
+ window.location.replace(`/web#action=website.website_preview&website_id=${encodeURIComponent(website.id)}`);
},
diff --git a/addons/website/static/src/js/content/website_root.js b/addons/website/static/src/js/content/website_root.js
index 2093628d9ce..b07848a726a 100644
--- a/addons/website/static/src/js/content/website_root.js
+++ b/addons/website/static/src/js/content/website_root.js
@@ -141,7 +141,7 @@ export const WebsiteRoot = publicRootData.PublicRoot.extend(KeyboardNavigationMi
this._gmapAPILoading = false;
return;
}
- await loadJS(`https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=places&callback=odoo_gmap_api_post_load&key=${key}`);
+ await loadJS(`https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=places&callback=odoo_gmap_api_post_load&key=${encodeURIComponent(key)}`);
});
}
return this._gmapAPILoading;
@@ -173,7 +173,7 @@ export const WebsiteRoot = publicRootData.PublicRoot.extend(KeyboardNavigationMi
var $target = $(ev.currentTarget);
// retrieve the hash before the redirect
var redirect = {
- lang: $target.data('url_code'),
+ lang: encodeURIComponent($target.data('url_code')),
url: encodeURIComponent($target.attr('href').replace(/[&?]edit_translations[^&?]+/, '')),
hash: encodeURIComponent(window.location.hash)
};
diff --git a/addons/website/static/src/js/editor/snippets.editor.js b/addons/website/static/src/js/editor/snippets.editor.js
index 2096557c49b..efc0b7d01db 100644
--- a/addons/website/static/src/js/editor/snippets.editor.js
+++ b/addons/website/static/src/js/editor/snippets.editor.js
@@ -169,7 +169,7 @@ const wSnippetMenu = weSnippetEditor.SnippetsMenu.extend({
*/
async _validateGMapAPIKey(key) {
try {
- const response = await fetch(`https://maps.googleapis.com/maps/api/staticmap?center=belgium&size=10x10&key=${key}`);
+ const response = await fetch(`https://maps.googleapis.com/maps/api/staticmap?center=belgium&size=10x10&key=${encodeURIComponent(key)}`);
const isValid = (response.status === 200);
return {
isValid: isValid,
diff --git a/addons/website/static/src/js/editor/snippets.options.js b/addons/website/static/src/js/editor/snippets.options.js
index 2670d072e97..3b659de8672 100644
--- a/addons/website/static/src/js/editor/snippets.options.js
+++ b/addons/website/static/src/js/editor/snippets.options.js
@@ -128,7 +128,7 @@ const FontFamilyPickerUserValueWidget = SelectUserValueWidget.extend({
}
for (const font of this.googleLocalFonts) {
const attachmentId = font.split(/\s*:\s*/)[1];
- const fontURL = `/web/content/${attachmentId}`;
+ const fontURL = `/web/content/${encodeURIComponent(attachmentId)}`;
fontsToLoad.push(fontURL);
}
// TODO ideally, remove the elements created once this widget
@@ -233,7 +233,9 @@ const FontFamilyPickerUserValueWidget = SelectUserValueWidget.extend({
let isValidFamily = false;
try {
- const result = await fetch("https://fonts.googleapis.com/css?family=" + m[1]+':300,300i,400,400i,700,700i', {method: 'HEAD'});
+ // Font family is an encoded query parameter:
+ // "Open+Sans" needs to remain "Open+Sans".
+ const result = await fetch("https://fonts.googleapis.com/css?family=" + m[1] + ':300,300i,400,400i,700,700i', {method: 'HEAD'});
// Google fonts server returns a 400 status code if family is not valid.
if (result.ok) {
isValidFamily = true;
@@ -1627,7 +1629,7 @@ options.registry.company_data = options.Class.extend({
args: [session.uid, ['company_id']],
});
}).then(function (res) {
- proto.__link = '/web#action=base.action_res_company_form&view_type=form&id=' + (res && res[0] && res[0].company_id[0] || 1);
+ proto.__link = '/web#action=base.action_res_company_form&view_type=form&id=' + encodeURIComponent(res && res[0] && res[0].company_id[0] || 1);
});
}
return Promise.all([this._super.apply(this, arguments), prom]);
diff --git a/addons/website/static/src/services/website_service.js b/addons/website/static/src/services/website_service.js
index 3a45e287391..77dfadbd130 100644
--- a/addons/website/static/src/services/website_service.js
+++ b/addons/website/static/src/services/website_service.js
@@ -193,7 +193,7 @@ export const websiteService = {
this.websiteRootInstance = undefined;
if (lang) {
invalidateSnippetCache = true;
- path = `/website/lang/${lang}?r=${encodeURIComponent(path)}`;
+ path = `/website/lang/${encodeURIComponent(lang)}?r=${encodeURIComponent(path)}`;
}
action.doAction('website.website_preview', {
clearBreadcrumbs: true,
diff --git a/addons/website/static/src/snippets/s_social_media/options.js b/addons/website/static/src/snippets/s_social_media/options.js
index c90cb256a37..462a6a568f8 100644
--- a/addons/website/static/src/snippets/s_social_media/options.js
+++ b/addons/website/static/src/snippets/s_social_media/options.js
@@ -128,7 +128,7 @@ options.registry.SocialMedia = options.Class.extend({
const faIcon = isDbField ? `fa-${entry.media}` : 'fa-pencil';
anchorEl.querySelector('i').classList.add(faIcon);
if (isDbField) {
- anchorEl.href = `/website/social/${entry.media}`;
+ anchorEl.href = `/website/social/${encodeURIComponent(entry.media)}`;
anchorEl.classList.add(`s_social_media_${entry.media}`);
}
}
@@ -199,7 +199,7 @@ options.registry.SocialMedia = options.Class.extend({
return {
id: generateHTMLId(),
display_name: media ? dbSocialValues[`social_${media}`] : el.getAttribute('href'),
- placeholder: `https://${media || 'example'}.com/yourPage`,
+ placeholder: `https://${encodeURIComponent(media) || 'example'}.com/yourPage`,
undeletable: !!media,
notToggleable: !media,
selected: true,
@@ -211,13 +211,13 @@ options.registry.SocialMedia = options.Class.extend({
// Adds the DB social media links that are not in the DOM.
for (let [media, link] of Object.entries(dbSocialValues)) {
media = media.split('social_').pop();
- if (!this.$target[0].querySelector(`:scope > a[href="/website/social/${media}"]`)) {
+ if (!this.$target[0].querySelector(`:scope > a[href="/website/social/${encodeURIComponent(media)}"]`)) {
const entryNotInDom = this.entriesNotInDom.find(entry => entry.media === media);
if (!entryNotInDom) {
this.entriesNotInDom.push({
id: generateHTMLId(),
display_name: link,
- placeholder: `https://${media}.com/yourPage`,
+ placeholder: `https://${encodeURIComponent(media)}.com/yourPage`,
undeletable: true,
selected: false,
listPosition: listPosition++,
diff --git a/addons/website/static/src/snippets/s_website_form/options.js b/addons/website/static/src/snippets/s_website_form/options.js
index 60cb4ce065d..702cef32461 100644
--- a/addons/website/static/src/snippets/s_website_form/options.js
+++ b/addons/website/static/src/snippets/s_website_form/options.js
@@ -770,7 +770,7 @@ options.registry.WebsiteFormEditor = FormEditor.extend({
* @param {string} action
*/
_redirectToAction: function (action) {
- window.location.replace(`/web#action=${action}`);
+ window.location.replace(`/web#action=${encodeURIComponent(action)}`);
},
//--------------------------------------------------------------------------
diff --git a/addons/website/static/src/systray_items/website_switcher.js b/addons/website/static/src/systray_items/website_switcher.js
index 3283c26f263..e18a2023fc7 100644
--- a/addons/website/static/src/systray_items/website_switcher.js
+++ b/addons/website/static/src/systray_items/website_switcher.js
@@ -21,7 +21,7 @@ export class WebsiteSwitcherSystray extends Component {
if (website.domain && !wUtils.isHTTPSorNakedDomainRedirection(website.domain, window.location.origin)) {
const { location: { pathname, search, hash } } = this.websiteService.contentWindow;
const path = pathname + search + hash;
- window.location.href = `${website.domain}/web#action=website.website_preview&path=${encodeURI(path)}&website_id=${website.id}`;
+ window.location.href = `${encodeURI(website.domain)}/web#action=website.website_preview&path=${encodeURIComponent(path)}&website_id=${encodeURIComponent(website.id)}`;
} else {
this.websiteService.goToWebsite({ websiteId: website.id });
}
diff --git a/addons/website_blog/static/src/js/website_blog.js b/addons/website_blog/static/src/js/website_blog.js
index 8fd216e0e13..82c0a6f913d 100644
--- a/addons/website_blog/static/src/js/website_blog.js
+++ b/addons/website_blog/static/src/js/website_blog.js
@@ -70,16 +70,16 @@ publicWidget.registry.websiteBlog = publicWidget.Widget.extend({
ev.preventDefault();
var url = '';
var $element = $(ev.currentTarget);
- var blogPostTitle = encodeURIComponent($('#o_wblog_post_name').html() || '');
- var articleURL = encodeURIComponent(window.location.href);
+ var blogPostTitle = $('#o_wblog_post_name').html() || '';
+ var articleURL = window.location.href;
if ($element.hasClass('o_twitter')) {
var twitterText = core._t("Amazing blog article: %s! Check it live: %s");
var tweetText = _.string.sprintf(twitterText, blogPostTitle, articleURL);
- url = 'https://twitter.com/intent/tweet?tw_p=tweetbutton&text=' + tweetText;
+ url = 'https://twitter.com/intent/tweet?tw_p=tweetbutton&text=' + encodeURIComponent(tweetText);
} else if ($element.hasClass('o_facebook')) {
- url = 'https://www.facebook.com/sharer/sharer.php?u=' + articleURL;
+ url = 'https://www.facebook.com/sharer/sharer.php?u=' + encodeURIComponent(articleURL);
} else if ($element.hasClass('o_linkedin')) {
- url = 'https://www.linkedin.com/sharing/share-offsite/?url=' + articleURL;
+ url = 'https://www.linkedin.com/sharing/share-offsite/?url=' + encodeURIComponent(articleURL);
}
window.open(url, '', 'menubar=no, width=500, height=400');
},
diff --git a/addons/website_event_booth/static/src/js/booth_register.js b/addons/website_event_booth/static/src/js/booth_register.js
index af7c5dfc8ec..3d6f0c7136f 100644
--- a/addons/website_event_booth/static/src/js/booth_register.js
+++ b/addons/website_event_booth/static/src/js/booth_register.js
@@ -204,7 +204,7 @@ publicWidget.registry.boothRegistration = publicWidget.Widget.extend({
if (this._isConfirmationFormValid($form)) {
const formData = new FormData($form[0]);
const response = await $.ajax({
- url: `/event/${this.$el.data('eventId')}/booth/confirm`,
+ url: `/event/${encodeURIComponent(this.$el.data('eventId'))}/booth/confirm`,
data: formData,
processData: false,
contentType: false,
diff --git a/addons/website_event_exhibitor/static/src/js/event_exhibitor_connect.js b/addons/website_event_exhibitor/static/src/js/event_exhibitor_connect.js
index 519981ae31d..da0a7e40d89 100644
--- a/addons/website_event_exhibitor/static/src/js/event_exhibitor_connect.js
+++ b/addons/website_event_exhibitor/static/src/js/event_exhibitor_connect.js
@@ -47,7 +47,7 @@ var ExhibitorConnectClosedDialog = Dialog.extend({
*/
async _fetchSponsor() {
const sponsorData = await this._rpc({
- route: `/event_sponsor/${this.sponsorId}/read`
+ route: `/event_sponsor/${encodeURIComponent(this.sponsorId)}/read`
});
sponsorData.website_description = Markup(sponsorData.website_description);
this.sponsorData = sponsorData;
diff --git a/addons/website_event_track/static/src/js/website_event_track_proposal_form.js b/addons/website_event_track/static/src/js/website_event_track_proposal_form.js
index ebe7558792c..d30a7cc9b62 100644
--- a/addons/website_event_track/static/src/js/website_event_track_proposal_form.js
+++ b/addons/website_event_track/static/src/js/website_event_track_proposal_form.js
@@ -171,7 +171,7 @@ publicWidget.registry.websiteEventTrackProposalForm = publicWidget.Widget.extend
const formData = new FormData(this.$el[0]);
const response = await $.ajax({
- url: `/event/${this.$el.data('eventId')}/track_proposal/post`,
+ url: `/event/${encodeURIComponent(this.$el.data('eventId'))}/track_proposal/post`,
data: formData,
processData: false,
contentType: false,
diff --git a/addons/website_forum/static/src/js/systray_items/forum_forum_add_form.js b/addons/website_forum/static/src/js/systray_items/forum_forum_add_form.js
index a1c992ab405..5c362badc6f 100644
--- a/addons/website_forum/static/src/js/systray_items/forum_forum_add_form.js
+++ b/addons/website_forum/static/src/js/systray_items/forum_forum_add_form.js
@@ -8,7 +8,7 @@ export class AddForumFormController extends NewContentFormController {
* @override
*/
computePath() {
- return `/forum/${this.model.root.data.id}`;
+ return `/forum/${encodeURIComponent(this.model.root.data.id)}`;
}
}
diff --git a/addons/website_forum/static/src/js/website_forum.js b/addons/website_forum/static/src/js/website_forum.js
index fe1c3560f1d..486075c874a 100644
--- a/addons/website_forum/static/src/js/website_forum.js
+++ b/addons/website_forum/static/src/js/website_forum.js
@@ -47,7 +47,7 @@ publicWidget.registry.websiteForum = publicWidget.Widget.extend({
// welcome message action button
var forumLogin = _.string.sprintf('%s/web?redirect=%s',
window.location.origin,
- escape(window.location.href)
+ encodeURIComponent(window.location.href)
);
$('.forum_register_url').attr('href', forumLogin);
@@ -252,7 +252,7 @@ publicWidget.registry.websiteForum = publicWidget.Widget.extend({
const linkLabel = _t("Read the guidelines to know how to gain karma.");
notifOptions.message = Markup`
${notifOptions.message}
- ${linkLabel}
+ ${linkLabel}
`;
}
}
diff --git a/addons/website_jitsi/static/src/js/chat_room.js b/addons/website_jitsi/static/src/js/chat_room.js
index 52ccb0ac471..dc821c189d2 100644
--- a/addons/website_jitsi/static/src/js/chat_room.js
+++ b/addons/website_jitsi/static/src/js/chat_room.js
@@ -230,7 +230,7 @@ publicWidget.registry.ChatRoom = publicWidget.Widget.extend({
_openMobileApplication: async function (roomName) {
if (config.device.isMobile) {
// we are on mobile, open the room in the application
- window.location = `intent://${this.jitsiServer}/${roomName}#Intent;scheme=org.jitsi.meet;package=org.jitsi.meet;end`;
+ window.location = `intent://${this.jitsiServer}/${encodeURIComponent(roomName)}#Intent;scheme=org.jitsi.meet;package=org.jitsi.meet;end`;
return true;
}
return false;
diff --git a/addons/website_livechat/static/src/core/persona_model_patch.js b/addons/website_livechat/static/src/core/persona_model_patch.js
index 1b9d026f9e9..f98a61c382e 100644
--- a/addons/website_livechat/static/src/core/persona_model_patch.js
+++ b/addons/website_livechat/static/src/core/persona_model_patch.js
@@ -7,7 +7,7 @@ patch(Persona.prototype, "website_livechat", {
get countryFlagUrl() {
const country = this.partner?.country ?? this.country;
return country
- ? `/base/static/img/country_flags/${country.code.toLowerCase()}.png`
+ ? `/base/static/img/country_flags/${encodeURIComponent(country.code.toLowerCase())}.png`
: undefined;
},
get nameOrDisplayName() {
diff --git a/addons/website_livechat/static/src/core/thread_service_patch.js b/addons/website_livechat/static/src/core/thread_service_patch.js
index 4b36cd364e2..eddc265bd2d 100644
--- a/addons/website_livechat/static/src/core/thread_service_patch.js
+++ b/addons/website_livechat/static/src/core/thread_service_patch.js
@@ -21,7 +21,7 @@ patch(ThreadService.prototype, "website_livechat", {
avatarUrl(persona, thread) {
if (persona?.type === "visitor" && thread?.id) {
return persona.partner
- ? `/mail/channel/${thread.id}/partner/${persona.id}/avatar_128`
+ ? `/mail/channel/${encodeURIComponent(thread.id)}/partner/${encodeURIComponent(persona.id)}/avatar_128`
: DEFAULT_AVATAR;
}
return this._super(persona, thread);
diff --git a/addons/website_sale_comparison/static/src/js/website_sale_comparison.js b/addons/website_sale_comparison/static/src/js/website_sale_comparison.js
index 999d8297054..80dc892e936 100644
--- a/addons/website_sale_comparison/static/src/js/website_sale_comparison.js
+++ b/addons/website_sale_comparison/static/src/js/website_sale_comparison.js
@@ -67,8 +67,8 @@ var ProductComparison = publicWidget.Widget.extend(VariantMixin, {
$(document.body).on('click.product_comparaison_widget', '.o_comparelist_remove', function (ev) {
self._removeFromComparelist(ev);
self.guard.exec(function() {
- var new_link = '/shop/compare?products=' + self.comparelist_product_ids.toString();
- window.location.href = _.isEmpty(self.comparelist_product_ids) ? '/shop' : new_link;
+ const newLink = '/shop/compare?products=' + encodeURIComponent(self.comparelist_product_ids);
+ window.location.href = _.isEmpty(self.comparelist_product_ids) ? '/shop' : newLink;
});
});
@@ -240,7 +240,8 @@ var ProductComparison = publicWidget.Widget.extend(VariantMixin, {
this.$('.o_comparelist_products').addClass('d-md-block');
if (this.comparelist_product_ids.length >=2) {
this.$('.o_comparelist_button').addClass('d-md-block');
- this.$('.o_comparelist_button a').attr('href', '/shop/compare?products='+this.comparelist_product_ids.toString());
+ this.$('.o_comparelist_button a').attr('href',
+ '/shop/compare?products=' + encodeURIComponent(this.comparelist_product_ids));
}
}
},
diff --git a/addons/website_slides/static/src/js/slides_course_fullscreen_player.js b/addons/website_slides/static/src/js/slides_course_fullscreen_player.js
index e1bfcf25abb..50ca540a39e 100644
--- a/addons/website_slides/static/src/js/slides_course_fullscreen_player.js
+++ b/addons/website_slides/static/src/js/slides_course_fullscreen_player.js
@@ -609,7 +609,7 @@
} else if (slideData.category === 'video' && slideData.videoSourceType === 'vimeo') {
slideData.embedCode = Markup(slideData.embedCode);
} else if (slideData.category === 'infographic') {
- slideData.embedUrl = `/web/image/slide.slide/${slideData.id}/image_1024`;
+ slideData.embedUrl = `/web/image/slide.slide/${encodeURIComponent(slideData.id)}/image_1024`;
} else if (slideData.category === 'document') {
slideData.embedUrl = $(slideData.embedCode).attr('src');
}
diff --git a/addons/website_slides/static/src/js/slides_course_join.js b/addons/website_slides/static/src/js/slides_course_join.js
index ab0bf8f4cf9..1f7b5acc11e 100644
--- a/addons/website_slides/static/src/js/slides_course_join.js
+++ b/addons/website_slides/static/src/js/slides_course_join.js
@@ -74,7 +74,7 @@ var CourseJoinWidget = publicWidget.Widget.extend({
url += '?fullscreen=1';
}
} else {
- url = `/slides/${this.channel.channelId}`;
+ url = `/slides/${encodeURIComponent(this.channel.channelId)}`;
}
document.location = sprintf('/web/login?redirect=%s', encodeURIComponent(url));
},
@@ -119,7 +119,7 @@ var CourseJoinWidget = publicWidget.Widget.extend({
const message = data.error_signup_allowed ?
_t('Please login or create an account to join this course') :
_t('Please login to join this course');
- self._popoverAlert(self.$el, sprintf(message, document.URL, document.URL));
+ self._popoverAlert(self.$el, sprintf(message, encodeURIComponent(document.URL), encodeURIComponent(document.URL)));
} else if (data.error === 'join_done') {
self._popoverAlert(self.$el, _t('You have already joined this channel'));
} else {
diff --git a/addons/website_slides/static/src/js/slides_course_quiz.js b/addons/website_slides/static/src/js/slides_course_quiz.js
index 8b7f8a1291a..6d503a16e6c 100644
--- a/addons/website_slides/static/src/js/slides_course_quiz.js
+++ b/addons/website_slides/static/src/js/slides_course_quiz.js
@@ -791,7 +791,7 @@
.text(_t('Mark To Do'))
.removeAttr('title')
.removeAttr('aria-disabled')
- .attr('href', `/slides/slide/${slide.id}/set_uncompleted`);
+ .attr('href', `/slides/slide/${encodeURIComponent(slide.id)}/set_uncompleted`);
}
},
diff --git a/addons/website_slides/static/src/js/slides_slide_like.js b/addons/website_slides/static/src/js/slides_slide_like.js
index f129a62ea8b..a6d64cd26ab 100644
--- a/addons/website_slides/static/src/js/slides_slide_like.js
+++ b/addons/website_slides/static/src/js/slides_slide_like.js
@@ -70,7 +70,7 @@ var SlideLikeWidget = publicWidget.Widget.extend({
const message = data.error_signup_allowed ?
_t('Please login or create an account to vote for this lesson') :
_t('Please login to vote for this lesson');
- self._popoverAlert(self.$el, sprintf(message, document.URL, document.URL));
+ self._popoverAlert(self.$el, sprintf(message, encodeURIComponent(document.URL), encodeURIComponent(document.URL)));
} else if (data.error === 'slide_access') {
self._popoverAlert(self.$el, _t('You don\'t have access to this lesson'));
} else if (data.error === 'channel_membership_required') {
diff --git a/addons/website_twitter/static/src/js/website.twitter.animation.js b/addons/website_twitter/static/src/js/website.twitter.animation.js
index d54e69029ca..5176a3ce40b 100644
--- a/addons/website_twitter/static/src/js/website.twitter.animation.js
+++ b/addons/website_twitter/static/src/js/website.twitter.animation.js
@@ -62,7 +62,7 @@ publicWidget.registry.twitter = publicWidget.Widget.extend({
.replace(
/[#]+[A-Za-z0-9_]+/g,
function (hashtag) {
- return _makeLink('http://twitter.com/search?q='+hashtag.replace('#',''), hashtag);
+ return _makeLink('http://twitter.com/search?q=' + encodeURIComponent(hashtag.replace('#', '')), hashtag);
}
));