From af75309b6562e400f8da7b6f9c286d1cda7c4c3b Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Sun, 8 Dec 2024 10:44:07 +0100 Subject: [PATCH] fix: Improve External Link Security - Meeds-io/MIPs#159 Prior to this change, the target external link of sidebar items was all time 'NEW TAB Strategy' and without a 'rel' attribute. This change will fix the behavior of 'External Link Target' in addition to the Security fix of missing 'rel' attribute. --- webapp/src/main/webapp/vue-apps/common/js/NavigationUtils.js | 5 +++++ .../vue-apps/sidebar/components/list/SidebarListItem.vue | 5 ++++- .../vue-apps/site-details/components/SiteNavigationItem.vue | 4 ++++ .../vue-apps/top-bar-menu/components/NavigationMenuItem.vue | 4 ++++ .../top-bar-menu/components/NavigationMenuSubItem.vue | 4 ++++ .../components/mobile/NavigationMobileMenuItem.vue | 4 ++++ .../components/mobile/NavigationMobileMenuSubItem.vue | 1 + 7 files changed, 26 insertions(+), 1 deletion(-) diff --git a/webapp/src/main/webapp/vue-apps/common/js/NavigationUtils.js b/webapp/src/main/webapp/vue-apps/common/js/NavigationUtils.js index 8b8dbe40cdf..63f19cbeb31 100644 --- a/webapp/src/main/webapp/vue-apps/common/js/NavigationUtils.js +++ b/webapp/src/main/webapp/vue-apps/common/js/NavigationUtils.js @@ -31,3 +31,8 @@ export function getNavigationNodeTarget(navigation) { navigation.nodeTarget = navigation?.target === 'SAME_TAB' && '_self' || '_blank'; return navigation.nodeTarget; } + +export function getNavigationNodeRel(navigation) { + navigation.nodeRel = navigation?.target === 'NEW_TAB' && 'nofollow noreferrer noopener' || null; + return navigation.nodeRel; +} diff --git a/webapp/src/main/webapp/vue-apps/sidebar/components/list/SidebarListItem.vue b/webapp/src/main/webapp/vue-apps/sidebar/components/list/SidebarListItem.vue index 3563b9e59b9..24d73692a9e 100644 --- a/webapp/src/main/webapp/vue-apps/sidebar/components/list/SidebarListItem.vue +++ b/webapp/src/main/webapp/vue-apps/sidebar/components/list/SidebarListItem.vue @@ -270,7 +270,10 @@ export default { const attributes = {}; if (this.isUrl) { attributes.href = this.url; - attributes.target = this.item.target; + attributes.target = this.target; + if (attributes.target === '_blank') { + attributes.rel = 'nofollow noreferrer noopener'; + } } return attributes; }, diff --git a/webapp/src/main/webapp/vue-apps/site-details/components/SiteNavigationItem.vue b/webapp/src/main/webapp/vue-apps/site-details/components/SiteNavigationItem.vue index f72d458ca8a..ce2a0abcf9a 100644 --- a/webapp/src/main/webapp/vue-apps/site-details/components/SiteNavigationItem.vue +++ b/webapp/src/main/webapp/vue-apps/site-details/components/SiteNavigationItem.vue @@ -19,6 +19,7 @@ @@ -123,6 +124,9 @@ export default { navigationNodeTarget() { return this.$navigationUtils.getNavigationNodeTarget(this.navigation); }, + navigationNodeRel() { + return this.$navigationUtils.getNavigationNodeRel(this.navigation); + }, isSelected() { return this.navigationNodeUri === this.selectedPath; }, diff --git a/webapp/src/main/webapp/vue-apps/top-bar-menu/components/mobile/NavigationMobileMenuItem.vue b/webapp/src/main/webapp/vue-apps/top-bar-menu/components/mobile/NavigationMobileMenuItem.vue index 701496f8632..0a41df53501 100644 --- a/webapp/src/main/webapp/vue-apps/top-bar-menu/components/mobile/NavigationMobileMenuItem.vue +++ b/webapp/src/main/webapp/vue-apps/top-bar-menu/components/mobile/NavigationMobileMenuItem.vue @@ -30,6 +30,7 @@ v-bind="attrs" :href="navigationNodeUri" :target="navigationNodeTarget" + :rel="navigationNodeRel" :disabled="!hasPage && !hasChildren" :link="hasPage" :aria-label="navigation.label" @@ -100,6 +101,9 @@ export default { navigationNodeTarget() { return this.$navigationUtils.getNavigationNodeTarget(this.navigation); }, + navigationNodeRel() { + return this.$navigationUtils.getNavigationNodeRel(this.navigation); + }, }, methods: { updateNavigationState(value) { diff --git a/webapp/src/main/webapp/vue-apps/top-bar-menu/components/mobile/NavigationMobileMenuSubItem.vue b/webapp/src/main/webapp/vue-apps/top-bar-menu/components/mobile/NavigationMobileMenuSubItem.vue index d4ddcf1b710..284852fb612 100644 --- a/webapp/src/main/webapp/vue-apps/top-bar-menu/components/mobile/NavigationMobileMenuSubItem.vue +++ b/webapp/src/main/webapp/vue-apps/top-bar-menu/components/mobile/NavigationMobileMenuSubItem.vue @@ -49,6 +49,7 @@ :key="nav.id" :href="nav.nodeUri || $navigationUtils.getNavigationNodeUri(baseSiteUri, nav)" :target="nav.nodeTarget || $navigationUtils.getNavigationNodeTarget(nav)" + :rel="nav.nodeRel || $navigationUtils.getNavigationNodeRel(nav)" :link="!!nav.pageKey" @click="checkLink(nav, $event)">