@blocklet/xss 0.2.6 → 0.2.8

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/cjs/index.d.ts CHANGED
@@ -8,6 +8,6 @@ declare const _default: {
8
8
  name?: string;
9
9
  type?: string;
10
10
  }) => boolean;
11
- sanitizeSvg: (svgContent: string) => string;
11
+ sanitizeSvg: (svgContent: string, options?: SanitizeOptions, svgOptions?: import("xss").IFilterXSSOptions) => string;
12
12
  };
13
13
  export default _default;
package/cjs/types.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as xss from 'xss';
2
2
  export interface SanitizeOptions extends xss.IFilterXSSOptions {
3
3
  allowedKeys?: string[];
4
+ preserveCase?: boolean;
4
5
  }
package/cjs/utils.d.ts CHANGED
@@ -1,6 +1,27 @@
1
+ import * as xss from 'xss';
1
2
  import { SanitizeOptions } from './types';
2
3
  export declare const initSanitize: (_options?: SanitizeOptions) => any;
3
- export declare const sanitizeSvg: (svgContent: string) => string;
4
+ export declare const svgWhiteList: {
5
+ svg: string[];
6
+ circle: string[];
7
+ ellipse: string[];
8
+ line: string[];
9
+ path: string[];
10
+ polygon: string[];
11
+ polyline: string[];
12
+ rect: string[];
13
+ g: string[];
14
+ text: string[];
15
+ defs: never[];
16
+ clipPath: string[];
17
+ mask: string[];
18
+ use: string[];
19
+ linearGradient: string[];
20
+ radialGradient: string[];
21
+ stop: string[];
22
+ pattern: string[];
23
+ };
24
+ export declare const sanitizeSvg: (svgContent: string, options?: SanitizeOptions, svgOptions?: xss.IFilterXSSOptions) => string;
4
25
  export declare const isSvgFile: (svgContent: string, file?: {
5
26
  name?: string;
6
27
  type?: string;
package/cjs/utils.js CHANGED
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.sanitizeSvg = exports.isSvgFile = exports.initSanitize = void 0;
6
+ exports.svgWhiteList = exports.sanitizeSvg = exports.isSvgFile = exports.initSanitize = void 0;
7
7
  var xss = _interopRequireWildcard(require("xss"));
8
8
  var _omit = _interopRequireDefault(require("lodash/omit"));
9
9
  var _path = _interopRequireDefault(require("path"));
@@ -66,7 +66,38 @@ const initSanitize = (_options = {}) => {
66
66
  return sanitize;
67
67
  };
68
68
  exports.initSanitize = initSanitize;
69
- const svgWhiteList = {
69
+ function preserveTagCase(content) {
70
+ const tagCaseMapping = {};
71
+ Object.keys(svgWhiteList).forEach(originalTag => {
72
+ const lowerCaseTag = originalTag.toLowerCase();
73
+ if (lowerCaseTag !== originalTag) {
74
+ tagCaseMapping[lowerCaseTag] = originalTag;
75
+ }
76
+ });
77
+ let result = content;
78
+ Object.entries(tagCaseMapping).forEach(([lowercase, original]) => {
79
+ result = result.replace(new RegExp(`<${lowercase}([^>]*)>`, "gi"), (_match, rest) => `<${original}${rest}>`);
80
+ result = result.replace(new RegExp(`</${lowercase}>`, "gi"), `</${original}>`);
81
+ });
82
+ return result;
83
+ }
84
+ function preserveAttrCase(tag, name, value, isWhiteAttr) {
85
+ if (isWhiteAttr) {
86
+ const originalTagKey = Object.keys(svgWhiteList).find(key => key.toLowerCase() === tag.toLowerCase());
87
+ if (originalTagKey) {
88
+ const originalAttrName = svgWhiteList[originalTagKey].find(attr => attr.toLowerCase() === name.toLowerCase());
89
+ if (originalAttrName) {
90
+ value = xss.safeAttrValue(tag, name, value);
91
+ if (value) {
92
+ return originalAttrName + '="' + value + '"';
93
+ } else {
94
+ return originalAttrName;
95
+ }
96
+ }
97
+ }
98
+ }
99
+ }
100
+ const svgWhiteList = exports.svgWhiteList = {
70
101
  svg: ["width", "height", "viewBox", "xmlns", "version", "preserveAspectRatio", "xml:space"],
71
102
  circle: ["cx", "cy", "r", "fill", "stroke", "stroke-width", "fill-opacity", "stroke-opacity"],
72
103
  ellipse: ["cx", "cy", "rx", "ry", "fill", "stroke", "stroke-width"],
@@ -114,13 +145,21 @@ const svgSanitizeOptions = {
114
145
  }
115
146
  }
116
147
  };
117
- const sanitizeSvg = svgContent => {
148
+ const sanitizeSvg = (svgContent, options, svgOptions) => {
118
149
  const isSvg = isSvgFile(svgContent);
119
150
  if (!isSvg) {
120
151
  throw new Error("Invalid SVG content");
121
152
  }
122
- const xssInstance = new xss.FilterXSS(svgSanitizeOptions);
123
- return xssInstance.process(svgContent);
153
+ const filterOptions = {
154
+ ...svgSanitizeOptions,
155
+ ...(svgOptions || {})
156
+ };
157
+ if (options?.preserveCase) {
158
+ filterOptions.onTagAttr = preserveAttrCase;
159
+ }
160
+ const xssInstance = new xss.FilterXSS(filterOptions);
161
+ const processedContent = xssInstance.process(svgContent);
162
+ return options?.preserveCase ? preserveTagCase(processedContent) : processedContent;
124
163
  };
125
164
  exports.sanitizeSvg = sanitizeSvg;
126
165
  const isSvgFile = (svgContent, file) => {
package/es/index.d.ts CHANGED
@@ -8,6 +8,6 @@ declare const _default: {
8
8
  name?: string;
9
9
  type?: string;
10
10
  }) => boolean;
11
- sanitizeSvg: (svgContent: string) => string;
11
+ sanitizeSvg: (svgContent: string, options?: SanitizeOptions, svgOptions?: import("xss").IFilterXSSOptions) => string;
12
12
  };
13
13
  export default _default;
package/es/types.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as xss from 'xss';
2
2
  export interface SanitizeOptions extends xss.IFilterXSSOptions {
3
3
  allowedKeys?: string[];
4
+ preserveCase?: boolean;
4
5
  }
package/es/utils.d.ts CHANGED
@@ -1,6 +1,27 @@
1
+ import * as xss from 'xss';
1
2
  import { SanitizeOptions } from './types';
2
3
  export declare const initSanitize: (_options?: SanitizeOptions) => any;
3
- export declare const sanitizeSvg: (svgContent: string) => string;
4
+ export declare const svgWhiteList: {
5
+ svg: string[];
6
+ circle: string[];
7
+ ellipse: string[];
8
+ line: string[];
9
+ path: string[];
10
+ polygon: string[];
11
+ polyline: string[];
12
+ rect: string[];
13
+ g: string[];
14
+ text: string[];
15
+ defs: never[];
16
+ clipPath: string[];
17
+ mask: string[];
18
+ use: string[];
19
+ linearGradient: string[];
20
+ radialGradient: string[];
21
+ stop: string[];
22
+ pattern: string[];
23
+ };
24
+ export declare const sanitizeSvg: (svgContent: string, options?: SanitizeOptions, svgOptions?: xss.IFilterXSSOptions) => string;
4
25
  export declare const isSvgFile: (svgContent: string, file?: {
5
26
  name?: string;
6
27
  type?: string;
package/es/utils.js CHANGED
@@ -80,7 +80,40 @@ export const initSanitize = (_options = {}) => {
80
80
  };
81
81
  return sanitize;
82
82
  };
83
- const svgWhiteList = {
83
+ function preserveTagCase(content) {
84
+ const tagCaseMapping = {};
85
+ Object.keys(svgWhiteList).forEach((originalTag) => {
86
+ const lowerCaseTag = originalTag.toLowerCase();
87
+ if (lowerCaseTag !== originalTag) {
88
+ tagCaseMapping[lowerCaseTag] = originalTag;
89
+ }
90
+ });
91
+ let result = content;
92
+ Object.entries(tagCaseMapping).forEach(([lowercase, original]) => {
93
+ result = result.replace(new RegExp(`<${lowercase}([^>]*)>`, "gi"), (_match, rest) => `<${original}${rest}>`);
94
+ result = result.replace(new RegExp(`</${lowercase}>`, "gi"), `</${original}>`);
95
+ });
96
+ return result;
97
+ }
98
+ function preserveAttrCase(tag, name, value, isWhiteAttr) {
99
+ if (isWhiteAttr) {
100
+ const originalTagKey = Object.keys(svgWhiteList).find((key) => key.toLowerCase() === tag.toLowerCase());
101
+ if (originalTagKey) {
102
+ const originalAttrName = svgWhiteList[originalTagKey].find(
103
+ (attr) => attr.toLowerCase() === name.toLowerCase()
104
+ );
105
+ if (originalAttrName) {
106
+ value = xss.safeAttrValue(tag, name, value);
107
+ if (value) {
108
+ return originalAttrName + '="' + value + '"';
109
+ } else {
110
+ return originalAttrName;
111
+ }
112
+ }
113
+ }
114
+ }
115
+ }
116
+ export const svgWhiteList = {
84
117
  svg: ["width", "height", "viewBox", "xmlns", "version", "preserveAspectRatio", "xml:space"],
85
118
  circle: ["cx", "cy", "r", "fill", "stroke", "stroke-width", "fill-opacity", "stroke-opacity"],
86
119
  ellipse: ["cx", "cy", "rx", "ry", "fill", "stroke", "stroke-width"],
@@ -128,13 +161,18 @@ const svgSanitizeOptions = {
128
161
  }
129
162
  }
130
163
  };
131
- export const sanitizeSvg = (svgContent) => {
164
+ export const sanitizeSvg = (svgContent, options, svgOptions) => {
132
165
  const isSvg = isSvgFile(svgContent);
133
166
  if (!isSvg) {
134
167
  throw new Error("Invalid SVG content");
135
168
  }
136
- const xssInstance = new xss.FilterXSS(svgSanitizeOptions);
137
- return xssInstance.process(svgContent);
169
+ const filterOptions = { ...svgSanitizeOptions, ...svgOptions || {} };
170
+ if (options?.preserveCase) {
171
+ filterOptions.onTagAttr = preserveAttrCase;
172
+ }
173
+ const xssInstance = new xss.FilterXSS(filterOptions);
174
+ const processedContent = xssInstance.process(svgContent);
175
+ return options?.preserveCase ? preserveTagCase(processedContent) : processedContent;
138
176
  };
139
177
  export const isSvgFile = (svgContent, file) => {
140
178
  if (typeof svgContent !== "string") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/xss",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "blocklet prevent xss attack",
5
5
  "publishConfig": {
6
6
  "access": "public"