2
0
mirror of https://github.com/tenrok/BBob.git synced 2026-05-15 11:59:37 +03:00

fix(html): escape bad html (#67)

* feat(preset-html5): add feature to filter javascript: urls

* fix(plugin-helper): escape html in attrs

* fix(plugin-helper): tests for html escape

* refactor(preset-html5): remove html escape from preset

* feat(preset): add ability to pass and extend preset options
This commit is contained in:
Nikolay Kostyurin
2020-07-05 15:23:22 +02:00
committed by GitHub
parent ba090bf997
commit 87f38fe97e
5 changed files with 63 additions and 15 deletions
+11 -3
View File
@@ -27,7 +27,14 @@ const appendToNode = (node, value) => {
* Replaces " to &qquot;
* @param {String} value
*/
const escapeQuote = (value) => value.replace(/"/g, '"');
const escapeHTML = (value) => value
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;')
// eslint-disable-next-line no-script-url
.replace('javascript:', 'javascript%3A');
/**
* Acept name and value and return valid html5 attribute string
@@ -41,8 +48,8 @@ const attrValue = (name, value) => {
const types = {
boolean: () => (value ? `${name}` : ''),
number: () => `${name}="${value}"`,
string: () => `${name}="${escapeQuote(value)}"`,
object: () => `${name}="${escapeQuote(JSON.stringify(value))}"`,
string: () => `${name}="${escapeHTML(value)}"`,
object: () => `${name}="${escapeHTML(JSON.stringify(value))}"`,
};
return types[type] ? types[type]() : '';
@@ -78,6 +85,7 @@ export {
attrsToString,
attrValue,
appendToNode,
escapeHTML,
getNodeLength,
getUniqAttr,
isTagNode,
+16 -1
View File
@@ -80,11 +80,26 @@ describe('@bbob/plugin-helper', () => {
disabled: true
})).toBe(` tag="test" foo="bar" disabled`)
});
test('attrsToString undefined', () => {
expect(attrsToString(undefined)).toBe('')
});
describe('attrsToString escape', () => {
test(`javascript:alert("hello")`, () => {
expect(attrsToString({
onclick: `javascript:alert('hello')`,
href: `javascript:alert('hello')`,
})).toBe(` onclick="javascript%3Aalert(&#039;hello&#039;)" href="javascript%3Aalert(&#039;hello&#039;)"`)
});
test(`<tag>`, () => {
expect(attrsToString({
onclick: `<tag>`,
href: `<tag>`,
})).toBe(` onclick="&lt;tag&gt;" href="&lt;tag&gt;"`)
});
});
test('getUniqAttr with unq attr', () => {
expect(getUniqAttr({foo: true, 'http://bar.com': 'http://bar.com'})).toBe('http://bar.com')
});
@@ -1,5 +1,5 @@
/* eslint-disable no-plusplus,no-lonely-if */
import { isStringNode, isTagNode, getUniqAttr } from '@bbob/plugin-helper';
import { getUniqAttr, isStringNode, isTagNode } from '@bbob/plugin-helper';
import TagNode from '@bbob/plugin-helper/lib/TagNode';
const isStartsWith = (node, type) => (node[0] === type);
@@ -55,6 +55,10 @@ const asListItems = (content) => {
return [].concat(listItems);
};
const renderUrl = (node, render) => (getUniqAttr(node.attrs)
? getUniqAttr(node.attrs)
: render(node.content));
export default {
b: (node) => ({
tag: 'span',
@@ -84,10 +88,10 @@ export default {
},
content: node.content,
}),
url: (node, { render }) => ({
url: (node, { render }, options) => ({
tag: 'a',
attrs: {
href: getUniqAttr(node.attrs) ? getUniqAttr(node.attrs) : render(node.content),
href: renderUrl(node, render, options),
},
content: node.content,
}),
+8 -3
View File
@@ -1,9 +1,9 @@
/* eslint-disable indent */
import { isTagNode } from '@bbob/plugin-helper';
function process(tags, tree, core) {
function process(tags, tree, core, options) {
tree.walk((node) => (isTagNode(node) && tags[node.tag]
? tags[node.tag](node, core)
? tags[node.tag](node, core, options)
: node));
}
@@ -14,7 +14,12 @@ function process(tags, tree, core) {
function createPreset(defTags) {
const instance = (opts = {}) => {
instance.options = Object.assign(instance.options || {}, opts);
return (tree, core) => process(defTags, tree, core);
const creator = (tree, core) => process(defTags, tree, core, instance.options);
creator.options = instance.options;
return creator;
};
instance.extend = (callback) => createPreset(callback(defTags, instance.options));
+21 -5
View File
@@ -1,17 +1,33 @@
import {createPreset} from "../src/index";
import { createPreset } from '../src/index';
describe('@bbob/preset', () => {
test('create', () => {
const preset = createPreset({ test: true });
expect(preset.extend).toBeDefined();
expect(preset).toBeInstanceOf(Function);
expect(preset.extend)
.toBeDefined();
expect(preset)
.toBeInstanceOf(Function);
});
test('extend', () => {
const preset = createPreset({ foo: true });
const newPreset = preset.extend(props => ({ bar: true }));
expect(preset).toBeInstanceOf(Function);
expect(newPreset).toBeInstanceOf(Function);
expect(preset)
.toBeInstanceOf(Function);
expect(newPreset)
.toBeInstanceOf(Function);
});
test('pass options', () => {
const preset = createPreset({ test: true });
const newPreset = preset.extend((props, options) => ({ bar: true }));
const instance = preset({ foo: 'bar' });
const instance2 = newPreset({ some: true });
expect(instance.options)
.toEqual({ foo: 'bar' });
expect(instance2.options)
.toEqual({ some: true });
});
});