@deepmetier/xss 0.0.0-stage → 1.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js ADDED
@@ -0,0 +1,118 @@
1
+ /**
2
+ * xss
3
+ *
4
+ * @author Zongmin Lei<leizongmin@gmail.com>
5
+ */
6
+
7
+ var DEFAULT = require("./default");
8
+ var parser = require("./parser");
9
+ var FilterXSS = require("./xss");
10
+
11
+ /**
12
+ * filter xss function
13
+ *
14
+ * @param {String} html
15
+ * @param {Object} options { whiteList, onTag, onTagAttr, onIgnoreTag, onIgnoreTagAttr, safeAttrValue, escapeHtml }
16
+ * @return {String}
17
+ */
18
+ function filterXSS(html, options) {
19
+ var xss = new FilterXSS(options);
20
+ return xss.process(html);
21
+ }
22
+
23
+ /**
24
+ * Filter XSS and return both the sanitized HTML and a list of removed elements.
25
+ * Addresses feature request: https://github.com/leizongmin/js-xss/issues/284
26
+ *
27
+ * @param {String} html - dirty HTML string to sanitize
28
+ * @param {Object} options - same options as filterXSS()
29
+ * @return {Object} { html: String, removed: Array }
30
+ *
31
+ * Each item in `removed` is one of:
32
+ * { type: "tag", tag: "script", html: "<script>", isClosing: false }
33
+ * { type: "attr", tag: "a", attr: "onclick", value: "evil()" }
34
+ */
35
+ function filterXSSWithResult(html, options) {
36
+ var removed = [];
37
+
38
+ // Copy options so we don't mutate the caller's object
39
+ var opts = {};
40
+ if (options) {
41
+ for (var key in options) {
42
+ opts[key] = options[key];
43
+ }
44
+ }
45
+
46
+ // Save any hooks the user may have already provided
47
+ var originalOnIgnoreTag = opts.onIgnoreTag;
48
+ var originalOnIgnoreTagAttr = opts.onIgnoreTagAttr;
49
+
50
+ // Override onIgnoreTag to record removed tags
51
+ opts.onIgnoreTag = function (tag, tagHtml, tagOptions) {
52
+ removed.push({
53
+ type: "tag",
54
+ tag: tag,
55
+ html: tagHtml,
56
+ isClosing: !!(tagOptions && tagOptions.isClosing),
57
+ });
58
+ // Still call the user's original hook if they had one
59
+ if (originalOnIgnoreTag) {
60
+ return originalOnIgnoreTag(tag, tagHtml, tagOptions);
61
+ }
62
+ };
63
+
64
+ // Override onIgnoreTagAttr to record removed attributes
65
+ opts.onIgnoreTagAttr = function (tag, name, value, isWhiteAttr) {
66
+ removed.push({
67
+ type: "attr",
68
+ tag: tag,
69
+ attr: name,
70
+ value: value,
71
+ });
72
+ // Still call the user's original hook if they had one
73
+ if (originalOnIgnoreTagAttr) {
74
+ return originalOnIgnoreTagAttr(tag, name, value, isWhiteAttr);
75
+ }
76
+ };
77
+
78
+ var cleanHtml = filterXSS(html, opts);
79
+
80
+ return {
81
+ html: cleanHtml,
82
+ removed: removed,
83
+ };
84
+ }
85
+ exports = module.exports = filterXSS;
86
+ exports.filterXSS = filterXSS;
87
+ exports.filterXSSWithResult = filterXSSWithResult;
88
+ exports.FilterXSS = FilterXSS;
89
+
90
+ (function () {
91
+ for (var i in DEFAULT) {
92
+ if (Object.prototype.hasOwnProperty.call(DEFAULT, i)) {
93
+ exports[i] = DEFAULT[i];
94
+ }
95
+ }
96
+ for (var j in parser) {
97
+ if (Object.prototype.hasOwnProperty.call(parser, j)) {
98
+ exports[j] = parser[j];
99
+ }
100
+ }
101
+ })();
102
+
103
+ // using `xss` on the browser, output `filterXSS` to the globals
104
+ if (typeof window !== "undefined") {
105
+ window.filterXSS = module.exports;
106
+ }
107
+
108
+ // using `xss` on the WebWorker, output `filterXSS` to the globals
109
+ function isWorkerEnv() {
110
+ return (
111
+ typeof self !== "undefined" &&
112
+ typeof DedicatedWorkerGlobalScope !== "undefined" &&
113
+ self instanceof DedicatedWorkerGlobalScope
114
+ );
115
+ }
116
+ if (isWorkerEnv()) {
117
+ self.filterXSS = module.exports;
118
+ }
package/lib/parser.js ADDED
@@ -0,0 +1,257 @@
1
+ /**
2
+ * Simple HTML Parser
3
+ *
4
+ * @author Zongmin Lei<leizongmin@gmail.com>
5
+ */
6
+
7
+ var _ = require("./util");
8
+
9
+ /**
10
+ * get tag name
11
+ *
12
+ * @param {String} html e.g. '<a hef="#">'
13
+ * @return {String}
14
+ */
15
+ function getTagName(html) {
16
+ var i = _.spaceIndex(html);
17
+ var tagName;
18
+ if (i === -1) {
19
+ tagName = html.slice(1, -1);
20
+ } else {
21
+ tagName = html.slice(1, i + 1);
22
+ }
23
+ tagName = _.trim(tagName).toLowerCase();
24
+ if (tagName.slice(0, 1) === "/") tagName = tagName.slice(1);
25
+ if (tagName.slice(-1) === "/") tagName = tagName.slice(0, -1);
26
+ return tagName;
27
+ }
28
+
29
+ /**
30
+ * is close tag?
31
+ *
32
+ * @param {String} html 如:'<a hef="#">'
33
+ * @return {Boolean}
34
+ */
35
+ function isClosing(html) {
36
+ return html.slice(0, 2) === "</";
37
+ }
38
+
39
+ /**
40
+ * parse input html and returns processed html
41
+ *
42
+ * @param {String} html
43
+ * @param {Function} onTag e.g. function (sourcePosition, position, tag, html, isClosing)
44
+ * @param {Function} escapeHtml
45
+ * @return {String}
46
+ */
47
+ function parseTag(html, onTag, escapeHtml) {
48
+ "use strict";
49
+
50
+ var rethtml = "";
51
+ var lastPos = 0;
52
+ var tagStart = false;
53
+ var quoteStart = false;
54
+ var currentPos = 0;
55
+ var len = html.length;
56
+ var currentTagName = "";
57
+ var currentHtml = "";
58
+
59
+ chariterator: for (currentPos = 0; currentPos < len; currentPos++) {
60
+ var c = html.charAt(currentPos);
61
+ if (tagStart === false) {
62
+ if (c === "<") {
63
+ tagStart = currentPos;
64
+ continue;
65
+ }
66
+ } else {
67
+ if (quoteStart === false) {
68
+ if (c === "<") {
69
+ rethtml += escapeHtml(html.slice(lastPos, currentPos));
70
+ tagStart = currentPos;
71
+ lastPos = currentPos;
72
+ continue;
73
+ }
74
+ if (c === ">" || currentPos === len - 1) {
75
+ rethtml += escapeHtml(html.slice(lastPos, tagStart));
76
+ currentHtml = html.slice(tagStart, currentPos + 1);
77
+ currentTagName = getTagName(currentHtml);
78
+ rethtml += onTag(
79
+ tagStart,
80
+ rethtml.length,
81
+ currentTagName,
82
+ currentHtml,
83
+ isClosing(currentHtml)
84
+ );
85
+ lastPos = currentPos + 1;
86
+ tagStart = false;
87
+ continue;
88
+ }
89
+ if (c === '"' || c === "'") {
90
+ var i = 1;
91
+ var ic = html.charAt(currentPos - i);
92
+
93
+ while (ic.trim() === "" || ic === "=") {
94
+ if (ic === "=") {
95
+ quoteStart = c;
96
+ continue chariterator;
97
+ }
98
+ ic = html.charAt(currentPos - ++i);
99
+ }
100
+ }
101
+ } else {
102
+ if (c === quoteStart) {
103
+ quoteStart = false;
104
+ continue;
105
+ }
106
+ }
107
+ }
108
+ }
109
+ if (lastPos < len) {
110
+ rethtml += escapeHtml(html.substr(lastPos));
111
+ }
112
+
113
+ return rethtml;
114
+ }
115
+
116
+ var REGEXP_ILLEGAL_ATTR_NAME = /[^a-zA-Z0-9\\_:.-]/gim;
117
+
118
+ /**
119
+ * parse input attributes and returns processed attributes
120
+ *
121
+ * @param {String} html e.g. `href="#" target="_blank"`
122
+ * @param {Function} onAttr e.g. `function (name, value)`
123
+ * @return {String}
124
+ */
125
+ function parseAttr(html, onAttr) {
126
+ "use strict";
127
+
128
+ var lastPos = 0;
129
+ var lastMarkPos = 0;
130
+ var retAttrs = [];
131
+ var tmpName = false;
132
+ var len = html.length;
133
+
134
+ function addAttr(name, value) {
135
+ name = _.trim(name);
136
+ name = name.replace(REGEXP_ILLEGAL_ATTR_NAME, "").toLowerCase();
137
+ if (name.length < 1) return;
138
+ var ret = onAttr(name, value || "");
139
+ if (ret) retAttrs.push(ret);
140
+ }
141
+
142
+ // 逐个分析字符
143
+ for (var i = 0; i < len; i++) {
144
+ var c = html.charAt(i);
145
+ var v, j;
146
+ if (tmpName === false && c === "=") {
147
+ tmpName = html.slice(lastPos, i);
148
+ lastPos = i + 1;
149
+ lastMarkPos = html.charAt(lastPos) === '"' || html.charAt(lastPos) === "'" ? lastPos : findNextQuotationMark(html, i + 1);
150
+ continue;
151
+ }
152
+ if (tmpName !== false) {
153
+ if (
154
+ i === lastMarkPos
155
+ ) {
156
+ j = html.indexOf(c, i + 1);
157
+ if (j === -1) {
158
+ break;
159
+ } else {
160
+ v = _.trim(html.slice(lastMarkPos + 1, j));
161
+ addAttr(tmpName, v);
162
+ tmpName = false;
163
+ i = j;
164
+ lastPos = i + 1;
165
+ continue;
166
+ }
167
+ }
168
+ }
169
+ if (/\s|\n|\t/.test(c)) {
170
+ html = html.replace(/\s|\n|\t/g, " ");
171
+ if (tmpName === false) {
172
+ j = findNextEqual(html, i);
173
+ if (j === -1) {
174
+ v = _.trim(html.slice(lastPos, i));
175
+ addAttr(v);
176
+ tmpName = false;
177
+ lastPos = i + 1;
178
+ continue;
179
+ } else {
180
+ i = j - 1;
181
+ continue;
182
+ }
183
+ } else {
184
+ j = findBeforeEqual(html, i - 1);
185
+ if (j === -1) {
186
+ v = _.trim(html.slice(lastPos, i));
187
+ v = stripQuoteWrap(v);
188
+ addAttr(tmpName, v);
189
+ tmpName = false;
190
+ lastPos = i + 1;
191
+ continue;
192
+ } else {
193
+ continue;
194
+ }
195
+ }
196
+ }
197
+ }
198
+
199
+ if (lastPos < html.length) {
200
+ if (tmpName === false) {
201
+ addAttr(html.slice(lastPos));
202
+ } else {
203
+ addAttr(tmpName, stripQuoteWrap(_.trim(html.slice(lastPos))));
204
+ }
205
+ }
206
+
207
+ return _.trim(retAttrs.join(" "));
208
+ }
209
+
210
+ function findNextEqual(str, i) {
211
+ for (; i < str.length; i++) {
212
+ var c = str[i];
213
+ if (c === " ") continue;
214
+ if (c === "=") return i;
215
+ return -1;
216
+ }
217
+ }
218
+
219
+ function findNextQuotationMark(str, i) {
220
+ for (; i < str.length; i++) {
221
+ var c = str[i];
222
+ if (c === " ") continue;
223
+ if (c === "'" || c === '"') return i;
224
+ return -1;
225
+ }
226
+ }
227
+
228
+ function findBeforeEqual(str, i) {
229
+ for (; i > 0; i--) {
230
+ var c = str[i];
231
+ if (c === " ") continue;
232
+ if (c === "=") return i;
233
+ return -1;
234
+ }
235
+ }
236
+
237
+ function isQuoteWrapString(text) {
238
+ if (
239
+ (text[0] === '"' && text[text.length - 1] === '"') ||
240
+ (text[0] === "'" && text[text.length - 1] === "'")
241
+ ) {
242
+ return true;
243
+ } else {
244
+ return false;
245
+ }
246
+ }
247
+
248
+ function stripQuoteWrap(text) {
249
+ if (isQuoteWrapString(text)) {
250
+ return text.substr(1, text.length - 2);
251
+ } else {
252
+ return text;
253
+ }
254
+ }
255
+
256
+ exports.parseTag = parseTag;
257
+ exports.parseAttr = parseAttr;
package/lib/util.js ADDED
@@ -0,0 +1,34 @@
1
+ module.exports = {
2
+ indexOf: function (arr, item) {
3
+ var i, j;
4
+ if (Array.prototype.indexOf) {
5
+ return arr.indexOf(item);
6
+ }
7
+ for (i = 0, j = arr.length; i < j; i++) {
8
+ if (arr[i] === item) {
9
+ return i;
10
+ }
11
+ }
12
+ return -1;
13
+ },
14
+ forEach: function (arr, fn, scope) {
15
+ var i, j;
16
+ if (Array.prototype.forEach) {
17
+ return arr.forEach(fn, scope);
18
+ }
19
+ for (i = 0, j = arr.length; i < j; i++) {
20
+ fn.call(scope, arr[i], i, arr);
21
+ }
22
+ },
23
+ trim: function (str) {
24
+ if (String.prototype.trim) {
25
+ return str.trim();
26
+ }
27
+ return str.replace(/(^\s*)|(\s*$)/g, "");
28
+ },
29
+ spaceIndex: function (str) {
30
+ var reg = /\s|\n|\t/;
31
+ var match = reg.exec(str);
32
+ return match ? match.index : -1;
33
+ },
34
+ };
package/lib/xss.js ADDED
@@ -0,0 +1,237 @@
1
+ /**
2
+ * filter xss
3
+ *
4
+ * @author Zongmin Lei<leizongmin@gmail.com>
5
+ */
6
+
7
+ var FilterCSS = require("@deepmetier/cssfilter").FilterCSS;
8
+ var DEFAULT = require("./default");
9
+ var parser = require("./parser");
10
+ var parseTag = parser.parseTag;
11
+ var parseAttr = parser.parseAttr;
12
+ var _ = require("./util");
13
+
14
+ /**
15
+ * returns `true` if the input value is `undefined` or `null`
16
+ *
17
+ * @param {Object} obj
18
+ * @return {Boolean}
19
+ */
20
+ function isNull(obj) {
21
+ return obj === undefined || obj === null;
22
+ }
23
+
24
+ /**
25
+ * get attributes for a tag
26
+ *
27
+ * @param {String} html
28
+ * @return {Object}
29
+ * - {String} html
30
+ * - {Boolean} closing
31
+ */
32
+ function getAttrs(html) {
33
+ var i = _.spaceIndex(html);
34
+ if (i === -1) {
35
+ return {
36
+ html: "",
37
+ closing: html[html.length - 2] === "/",
38
+ };
39
+ }
40
+ html = _.trim(html.slice(i + 1, -1));
41
+ var isClosing = html[html.length - 1] === "/";
42
+ if (isClosing) html = _.trim(html.slice(0, -1));
43
+ return {
44
+ html: html,
45
+ closing: isClosing,
46
+ };
47
+ }
48
+
49
+ /**
50
+ * shallow copy
51
+ *
52
+ * @param {Object} obj
53
+ * @return {Object}
54
+ */
55
+ function shallowCopyObject(obj) {
56
+ var ret = {};
57
+ for (var i in obj) {
58
+ if (Object.prototype.hasOwnProperty.call(obj, i)) {
59
+ ret[i] = obj[i];
60
+ }
61
+ }
62
+ return ret;
63
+ }
64
+
65
+ function keysToLowerCase(obj) {
66
+ var ret = {};
67
+ for (var i in obj) {
68
+ if (Object.prototype.hasOwnProperty.call(obj, i)) {
69
+ if (Array.isArray(obj[i])) {
70
+ ret[i.toLowerCase()] = obj[i].map(function (item) {
71
+ return item.toLowerCase();
72
+ });
73
+ } else {
74
+ ret[i.toLowerCase()] = obj[i];
75
+ }
76
+ }
77
+ }
78
+ return ret;
79
+ }
80
+
81
+ /**
82
+ * FilterXSS class
83
+ *
84
+ * @param {Object} options
85
+ * whiteList (or allowList), onTag, onTagAttr, onIgnoreTag,
86
+ * onIgnoreTagAttr, safeAttrValue, escapeHtml
87
+ * stripIgnoreTagBody, allowCommentTag, stripBlankChar
88
+ * css{whiteList, onAttr, onIgnoreAttr} `css=false` means don't use `cssfilter`
89
+ */
90
+ function FilterXSS(options) {
91
+ options = shallowCopyObject(options || {});
92
+
93
+ if (Object.prototype.hasOwnProperty.call(options, 'stripIgnoreTag') && options.stripIgnoreTag) {
94
+ if (Object.prototype.hasOwnProperty.call(options, 'onIgnoreTag') && options.onIgnoreTag) {
95
+ console.error(
96
+ 'Notes: cannot use these two options "stripIgnoreTag" and "onIgnoreTag" at the same time'
97
+ );
98
+ }
99
+ options.onIgnoreTag = DEFAULT.onIgnoreTagStripAll;
100
+ }
101
+ if (Object.prototype.hasOwnProperty.call(options, 'whiteList') || Object.prototype.hasOwnProperty.call(options, 'allowList')) {
102
+ const wl = Object.prototype.hasOwnProperty.call(options, 'whiteList') ? options.whiteList : options.allowList;
103
+ options.whiteList = keysToLowerCase(wl);
104
+ } else {
105
+ options.whiteList = DEFAULT.whiteList;
106
+ }
107
+
108
+ this.attributeWrapSign = Object.prototype.hasOwnProperty.call(options, 'singleQuotedAttributeValue') && options.singleQuotedAttributeValue === true ? "'" : DEFAULT.attributeWrapSign;
109
+
110
+ options.onTag = Object.prototype.hasOwnProperty.call(options, 'onTag') ? options.onTag : DEFAULT.onTag;
111
+ options.onTagAttr = Object.prototype.hasOwnProperty.call(options, 'onTagAttr') ? options.onTagAttr : DEFAULT.onTagAttr;
112
+ options.onIgnoreTag = Object.prototype.hasOwnProperty.call(options, 'onIgnoreTag') ? options.onIgnoreTag : DEFAULT.onIgnoreTag;
113
+ options.onIgnoreTagAttr = Object.prototype.hasOwnProperty.call(options, 'onIgnoreTagAttr') ? options.onIgnoreTagAttr : DEFAULT.onIgnoreTagAttr;
114
+ options.safeAttrValue = Object.prototype.hasOwnProperty.call(options, 'safeAttrValue') ? options.safeAttrValue : DEFAULT.safeAttrValue;
115
+ options.escapeHtml = Object.prototype.hasOwnProperty.call(options, 'escapeHtml') ? options.escapeHtml : DEFAULT.escapeHtml;
116
+ this.options = options;
117
+
118
+ if (Object.prototype.hasOwnProperty.call(options, 'css') && options.css === false) {
119
+ this.cssFilter = false;
120
+ } else {
121
+ options.css = Object.prototype.hasOwnProperty.call(options, 'css') ? options.css : {};
122
+ this.cssFilter = new FilterCSS(options.css);
123
+ }
124
+ }
125
+
126
+ /**
127
+ * start process and returns result
128
+ *
129
+ * @param {String} html
130
+ * @return {String}
131
+ */
132
+ FilterXSS.prototype.process = function (html) {
133
+ // compatible with the input
134
+ html = html || "";
135
+ html = html.toString();
136
+ if (!html) return "";
137
+
138
+ var me = this;
139
+ var options = me.options;
140
+ var whiteList = options.whiteList;
141
+ var onTag = options.onTag;
142
+ var onIgnoreTag = options.onIgnoreTag;
143
+ var onTagAttr = options.onTagAttr;
144
+ var onIgnoreTagAttr = options.onIgnoreTagAttr;
145
+ var safeAttrValue = options.safeAttrValue;
146
+ var escapeHtml = options.escapeHtml;
147
+ var attributeWrapSign = me.attributeWrapSign;
148
+ var cssFilter = me.cssFilter;
149
+
150
+ // remove invisible characters
151
+ if (options.stripBlankChar) {
152
+ html = DEFAULT.stripBlankChar(html);
153
+ }
154
+
155
+ // remove html comments
156
+ if (!options.allowCommentTag) {
157
+ html = DEFAULT.stripCommentTag(html);
158
+ }
159
+
160
+ // if enable stripIgnoreTagBody
161
+ var stripIgnoreTagBody = false;
162
+ if (options.stripIgnoreTagBody) {
163
+ stripIgnoreTagBody = DEFAULT.StripTagBody(
164
+ options.stripIgnoreTagBody,
165
+ onIgnoreTag
166
+ );
167
+ onIgnoreTag = stripIgnoreTagBody.onIgnoreTag;
168
+ }
169
+
170
+ var retHtml = parseTag(
171
+ html,
172
+ function (sourcePosition, position, tag, html, isClosing) {
173
+ var info = {
174
+ sourcePosition: sourcePosition,
175
+ position: position,
176
+ isClosing: isClosing,
177
+ isWhite: Object.prototype.hasOwnProperty.call(whiteList, tag),
178
+ };
179
+
180
+ // call `onTag()`
181
+ var ret = onTag(tag, html, info);
182
+ if (!isNull(ret)) return ret;
183
+
184
+ if (info.isWhite) {
185
+ if (info.isClosing) {
186
+ return "</" + tag + ">";
187
+ }
188
+
189
+ var attrs = getAttrs(html);
190
+ var whiteAttrList = whiteList[tag];
191
+ var attrsHtml = parseAttr(attrs.html, function (name, value) {
192
+ // call `onTagAttr()`
193
+ var isWhiteAttr = _.indexOf(whiteAttrList, name) !== -1;
194
+ var ret = onTagAttr(tag, name, value, isWhiteAttr);
195
+ if (!isNull(ret)) return ret;
196
+
197
+ if (isWhiteAttr) {
198
+ // call `safeAttrValue()`
199
+ value = safeAttrValue(tag, name, value, cssFilter);
200
+ if (value) {
201
+ return name + '=' + attributeWrapSign + value + attributeWrapSign;
202
+ } else {
203
+ return name;
204
+ }
205
+ } else {
206
+ // call `onIgnoreTagAttr()`
207
+ ret = onIgnoreTagAttr(tag, name, value, isWhiteAttr);
208
+ if (!isNull(ret)) return ret;
209
+ return;
210
+ }
211
+ });
212
+
213
+ // build new tag html
214
+ html = "<" + tag;
215
+ if (attrsHtml) html += " " + attrsHtml;
216
+ if (attrs.closing) html += " /";
217
+ html += ">";
218
+ return html;
219
+ } else {
220
+ // call `onIgnoreTag()`
221
+ ret = onIgnoreTag(tag, html, info);
222
+ if (!isNull(ret)) return ret;
223
+ return escapeHtml(html);
224
+ }
225
+ },
226
+ escapeHtml
227
+ );
228
+
229
+ // if enable stripIgnoreTagBody
230
+ if (stripIgnoreTagBody) {
231
+ retHtml = stripIgnoreTagBody.remove(retHtml);
232
+ }
233
+
234
+ return retHtml;
235
+ };
236
+
237
+ module.exports = FilterXSS;
package/package.json CHANGED
@@ -1,6 +1,65 @@
1
1
  {
2
2
  "name": "@deepmetier/xss",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "main": "./lib/index.js",
4
+ "typings": "./typings/xss.d.ts",
5
+ "version": "1.0.16",
6
+ "description": "Sanitize untrusted HTML (to prevent XSS) with a configuration specified by a Whitelist",
7
+ "author": "Zongmin Lei <leizongmin@gmail.com> (http://ucdok.com)",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git://github.com/leizongmin/js-xss.git"
11
+ },
12
+ "engines": {
13
+ "node": ">= 0.10.0"
14
+ },
15
+ "dependencies": {
16
+ "@deepmetier/commander": "^2.20.3",
17
+ "@deepmetier/cssfilter": "0.0.10"
18
+ },
19
+ "devDependencies": {
20
+ "browserify": "^17.0.0",
21
+ "coveralls": "^3.1.1",
22
+ "debug": "^4.3.4",
23
+ "eslint": "^8.16.0",
24
+ "mocha": "^8.4.0",
25
+ "nyc": "^15.1.0",
26
+ "uglify-js": "^3.15.5"
27
+ },
28
+ "files": [
29
+ "lib",
30
+ "bin/xss",
31
+ "dist",
32
+ "typings/*.d.ts"
33
+ ],
34
+ "bin": {
35
+ "xss": "./bin/xss"
36
+ },
37
+ "scripts": {
38
+ "lint": "eslint lib/**",
39
+ "test": "export DEBUG=xss:* && mocha -t 5000",
40
+ "test-cov": "nyc --reporter=lcov mocha --exit \"test/*.js\" && nyc report",
41
+ "coveralls": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js",
42
+ "build": "./bin/build",
43
+ "prepublish": "npm run test && npm run build"
44
+ },
45
+ "license": "MIT",
46
+ "bugs": {
47
+ "url": "https://github.com/leizongmin/js-xss/issues"
48
+ },
49
+ "homepage": "https://github.com/leizongmin/js-xss",
50
+ "keywords": [
51
+ "sanitization",
52
+ "xss",
53
+ "sanitize",
54
+ "sanitisation",
55
+ "input",
56
+ "security",
57
+ "escape",
58
+ "encode",
59
+ "filter",
60
+ "validator",
61
+ "html",
62
+ "injection",
63
+ "whitelist"
64
+ ]
65
+ }