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

Add support for stripping all tags #98

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
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
37 changes: 32 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ Vue.use(VueSafeHTML);

In your component:

```html
```jsx
LostCrew marked this conversation as resolved.
Show resolved Hide resolved
<template>
<div v-safe-html="myUnsafeHTML">
<div v-safe-html="myUnsafeHTML" />
</template>
```

Expand Down Expand Up @@ -91,13 +91,40 @@ Vue.use(VueSafeHTML, {

It is also possible to provide custom allowed tags directly to the directive tag, using directive modifiers. This allows local override of the option:

```html
```jsx
<template>
<div v-safe-html.p.strong="myUnsafeHTML" />
</template>
```

> Only allow p and strong tags

#### Stripping all tags for HTML entity decoding

To decode HTML entities only with no tags you can use the `strip-tags` directive argument:

```jsx
<template>
<!-- only allow p and strong tags -->
<div v-safe-html.p.strong="myUnsafeHTML">
<div v-safe-html:strip-tags="unsafeHTML" />
</template>
```

```js
export default {
computed: {
myUnsafeHTML() {
return '<p><strong>Cats<strong> &amp; <em>&quot;Dogs&quot;</em></p>';
}
}
}
```

Renders to:

```html
<div>Cats &amp; "Dogs"</div>
LostCrew marked this conversation as resolved.
Show resolved Hide resolved
```

### Nuxt

`vue-safe-html` is written as a Vue plugin so you can easily use it inside Nuxt by following [the Nuxt documentation](https://nuxtjs.org/docs/2.x/directory-structure/plugins#vue-plugins).
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vue-safe-html",
"version": "2.2.0",
"version": "2.3.0",
"description": "A Vue directive which renders sanitised HTML dynamically",
"main": "dist/main.js",
"repository": "[email protected]:ecosia/vue-safe-html.git",
Expand Down
29 changes: 19 additions & 10 deletions src/directive.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,28 @@ const areTagsValid = (tags) => (

export { defaultTags as allowedTags };

export default (tags) => {
const initialTags = areTagsValid(tags) ? tags : defaultTags;
return (el, binding) => {
let finalTags = initialTags;
const getAllowedTags = (initialTags, binding) => {
if (binding?.arg === 'strip-tags') {
LostCrew marked this conversation as resolved.
Show resolved Hide resolved
// v-safe-html:strip-tags returns no tags
return [];
}

if (binding.modifiers) {
const directiveTags = Object.keys(binding.modifiers);
if (directiveTags.length > 0 && areTagsValid(directiveTags)) {
finalTags = directiveTags;
}
if (binding.modifiers) {
// v-safe-html.p.strong returns 'p' and 'strong'
const directiveTags = Object.keys(binding.modifiers);
if (directiveTags.length > 0 && areTagsValid(directiveTags)) {
return directiveTags;
}
}

return initialTags;
};

const sanitized = sanitizeHTML(binding.value, finalTags);
export default (tags) => {
const initialTags = areTagsValid(tags) ? tags : defaultTags;
return (el, binding) => {
const allowedTags = getAllowedTags(initialTags, binding);
const sanitized = sanitizeHTML(binding.value, allowedTags);

if (typeof el.innerHTML === 'string') {
// we're client-side and `el` is an HTMLElement
Expand Down
38 changes: 38 additions & 0 deletions src/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,43 @@ describe('Plugin', () => {
const expected = '<div><section><strong>Safe</strong></section> HTML</div>';
expect(wrapper.element.outerHTML).toBe(expected);
});

describe('Modifiers', () => {
it('Handles allowed tags as modifiers', () => {
const localVue = createLocalVue();
localVue.use(Plugin, {
allowedTags: [
...defaultAllowedTags,
'section',
],
});
// eslint-disable-next-line vue/one-component-per-file
const Component = localVue.component('SafeHtmlComponent', {
template: '<div v-safe-html.p.strong="\'<p><section><strong>Safe</strong></section> HTML<script></script> &amp; <mark>marked text</mark></p>\'"></div>',
});
const wrapper = shallowMount(Component, { localVue });
const expected = '<div><p><strong>Safe</strong> HTML &amp; marked text</p></div>';
expect(wrapper.element.outerHTML).toBe(expected);
});
});

describe('"strip-tags" argument', () => {
LostCrew marked this conversation as resolved.
Show resolved Hide resolved
it('strips all HTML but leaves HTML entities', () => {
const localVue = createLocalVue();
localVue.use(Plugin, {
allowedTags: [
...defaultAllowedTags,
'section',
],
});
// eslint-disable-next-line vue/one-component-per-file
const Component = localVue.component('SafeHtmlComponent', {
template: '<div v-safe-html:strip-tags="\'<p><strong>Cats<strong> &amp; <em>&quot;Dogs&quot;</em></p>\'"></div>',
});
const wrapper = shallowMount(Component, { localVue });
const expected = '<div>Cats &amp; "Dogs"</div>';
expect(wrapper.element.outerHTML).toBe(expected);
});
});
});
});