diff --git a/README.md b/README.md
index b40bdaf..9471fbf 100644
--- a/README.md
+++ b/README.md
@@ -280,6 +280,20 @@ function safeAttrValue(tag, name, value) {
}
```
+### Customize output attribute value syntax for HTML
+
+By specifying a `singleQuotedAttributeValue`. Use `true` for `'`. Otherwise default `"` will be used
+
+```javascript
+var options = {
+ singleQuotedAttributeValue: true,
+};
+// With the configuration specified above, the following HTML:
+// Hello
+// would become:
+// Hello
+```
+
### Customize CSS filter
If you allow the attribute `style`, the value will be processed by [cssfilter](https://github.com/leizongmin/js-css-filter) module. The cssfilter module includes a default css whitelist. You can specify the options for cssfilter module like this:
diff --git a/lib/default.js b/lib/default.js
index 142f58e..9fa0536 100644
--- a/lib/default.js
+++ b/lib/default.js
@@ -455,5 +455,6 @@ exports.onIgnoreTagStripAll = onIgnoreTagStripAll;
exports.StripTagBody = StripTagBody;
exports.stripCommentTag = stripCommentTag;
exports.stripBlankChar = stripBlankChar;
+exports.attributeWrapSign = '"';
exports.cssFilter = defaultCSSFilter;
exports.getDefaultCSSWhiteList = getDefaultCSSWhiteList;
diff --git a/lib/xss.js b/lib/xss.js
index ed81049..b78cec4 100644
--- a/lib/xss.js
+++ b/lib/xss.js
@@ -100,6 +100,8 @@ function FilterXSS(options) {
options.whiteList = DEFAULT.whiteList;
}
+ this.attributeWrapSign = options.singleQuotedAttributeValue === true ? "'" : DEFAULT.attributeWrapSign;
+
options.onTag = options.onTag || DEFAULT.onTag;
options.onTagAttr = options.onTagAttr || DEFAULT.onTagAttr;
options.onIgnoreTag = options.onIgnoreTag || DEFAULT.onIgnoreTag;
@@ -137,6 +139,7 @@ FilterXSS.prototype.process = function (html) {
var onIgnoreTagAttr = options.onIgnoreTagAttr;
var safeAttrValue = options.safeAttrValue;
var escapeHtml = options.escapeHtml;
+ var attributeWrapSign = me.attributeWrapSign;
var cssFilter = me.cssFilter;
// remove invisible characters
@@ -190,7 +193,7 @@ FilterXSS.prototype.process = function (html) {
// call `safeAttrValue()`
value = safeAttrValue(tag, name, value, cssFilter);
if (value) {
- return name + '="' + value + '"';
+ return name + '=' + attributeWrapSign + value + attributeWrapSign;
} else {
return name;
}
diff --git a/test/test_xss.js b/test/test_xss.js
index 5a668b2..ca0e6f2 100644
--- a/test/test_xss.js
+++ b/test/test_xss.js
@@ -428,6 +428,22 @@ describe("test XSS", function() {
);
});
+ it("#singleQuotedAttributeValue", function() {
+ assert.equal(xss('not-defined'), 'not-defined');
+ assert.equal(
+ xss('single-quoted', { singleQuotedAttributeValue: true }),
+ 'single-quoted'
+ );
+ assert.equal(
+ xss('double-quoted', { singleQuotedAttributeValue: false }),
+ 'double-quoted'
+ );
+ assert.equal(
+ xss('invalid-value', { singleQuotedAttributeValue: 'invalid' }),
+ 'invalid-value'
+ );
+ })
+
it("no options mutated", function() {
var options = {};
diff --git a/typings/xss.d.ts b/typings/xss.d.ts
index f55d44a..bdb6220 100644
--- a/typings/xss.d.ts
+++ b/typings/xss.d.ts
@@ -22,6 +22,7 @@ declare module "xss" {
stripIgnoreTagBody?: boolean | string[];
allowCommentTag?: boolean;
stripBlankChar?: boolean;
+ singleQuotedAttributeValue?: boolean;
css?: {} | boolean;
}
@@ -195,6 +196,7 @@ declare module "xss" {
export function onIgnoreTagStripAll(): string;
export const stripCommentTag: EscapeHandler;
export const stripBlankChar: EscapeHandler;
+ export const attributeWrapSign: string;
export const cssFilter: ICSSFilter;
export function getDefaultCSSWhiteList(): ICSSFilter;