-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[LuxMenuBar] Add new unsafe_name property to the menu item object
`unsafe_name` works like the existing name property, except that if it contains some arbitrary HTML, it does not escape it. The immediate use case for this behavior is the "Bookmarks (4)" menu item in the catalog, which has a span with a specific class around the number that allows Blacklight's javascript to update it as the user adds/removes bookmarks. This has security implications, since `unsafe_name` bypasses Vue's cross-site scripting protections. I've added documentation about this (as well as other properties on this object that did not yet have this documentation). Interestingly, the `buttons` version of LuxMenuBar was already bypassing cross-site scripting protections for the `name` property. In the interest of consistency and safe defaults, this commit make it match the behavior of the others: you can add arbitrary HTML with `unsafe_name`, not with `name`. Helps with pulibrary/orangelight#4088
- Loading branch information
1 parent
9e5f43e
commit b0d5726
Showing
5 changed files
with
132 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<template> | ||
<span v-if="item.unsafe_name" v-html="item.unsafe_name"></span | ||
><template v-else>{{ item.name }}</template> | ||
</template> | ||
<script setup> | ||
defineProps({ | ||
item: Object, | ||
}) | ||
</script> | ||
<docs> | ||
```jsx | ||
<ul> | ||
<li><lux-menu-bar-label :item=" | ||
{name: 'Logout', href: '/logout'} | ||
"/></li> | ||
<li><lux-menu-bar-label :item=" | ||
{unsafe_name: 'Bookmarks <strong>(1 / 3)</strong>', href: '/logout'} | ||
"/></li> | ||
``` | ||
Security considerations: | ||
<ul> | ||
<li>You can add any arbitrary HTML to the <code>unsafe_name</code> property, | ||
and it will be rendered for the user. If you don't need to add arbitrary | ||
HTML, use the <code>name</code> property instead. Don't bind the | ||
<code>unsafe_name</code> property to any user-provided value, since it does | ||
not have Cross-Site Scripting protections (<code>name</code> does have these | ||
protections). | ||
</li> | ||
</ul> | ||
</docs> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { mount } from "@vue/test-utils" | ||
import _LuxMenuBarLabel from "@/components/_LuxMenuBarLabel.vue" | ||
|
||
describe("_LuxMenuBarLabel", () => { | ||
let wrapper | ||
describe("when item prop has a name", () => { | ||
beforeEach(() => { | ||
wrapper = mount(_LuxMenuBarLabel, { | ||
props: { | ||
item: { name: "Hello!" }, | ||
}, | ||
}) | ||
}) | ||
it("displays the name", () => { | ||
expect(wrapper.text()).toEqual("Hello!") | ||
}) | ||
}) | ||
describe("when item prop name contains HTML", () => { | ||
beforeEach(() => { | ||
wrapper = mount(_LuxMenuBarLabel, { | ||
props: { | ||
item: { name: '<script>alert("Bad!")</script>' }, | ||
}, | ||
}) | ||
}) | ||
it("escapes the HTML, so that it displays as text rather than being potentially unsafe", () => { | ||
expect(wrapper.text()).toEqual('<script>alert("Bad!")</script>') | ||
expect(wrapper.find("script").exists()).toBe(false) | ||
}) | ||
}) | ||
describe("when item prop has an unsafe_name", () => { | ||
beforeEach(() => { | ||
wrapper = mount(_LuxMenuBarLabel, { | ||
props: { | ||
item: { unsafe_name: "<span>Hello!</span>" }, | ||
}, | ||
}) | ||
}) | ||
it("does not escape the HTML", () => { | ||
expect(wrapper.text()).toEqual("Hello!") | ||
expect(wrapper.find("span").exists()).toBe(true) | ||
}) | ||
}) | ||
describe("when item prop has both a name and an unsafe_name", () => { | ||
beforeEach(() => { | ||
wrapper = mount(_LuxMenuBarLabel, { | ||
props: { | ||
item: { | ||
name: "Goodbye", | ||
unsafe_name: "<span>Hello!</span>", | ||
}, | ||
}, | ||
}) | ||
}) | ||
it("uses the unsafe name", () => { | ||
expect(wrapper.text()).toEqual("Hello!") | ||
expect(wrapper.find("span").exists()).toBe(true) | ||
}) | ||
}) | ||
}) |