svg-toolbox 1.1.12 → 1.2.0

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.
Files changed (42) hide show
  1. package/README.md +105 -87
  2. package/es/analyze/paths.js +14 -0
  3. package/es/analyze/paths.js.map +1 -1
  4. package/es/compare/__tests__/diff.test.d.ts +1 -0
  5. package/es/compare/__tests__/diff.test.js +64 -0
  6. package/es/compare/__tests__/diff.test.js.map +1 -0
  7. package/es/compare/diff.js +13 -7
  8. package/es/compare/diff.js.map +1 -1
  9. package/es/convert/image.js +10 -5
  10. package/es/convert/image.js.map +1 -1
  11. package/es/optimize/__tests__/cleanup.test.js +29 -0
  12. package/es/optimize/__tests__/cleanup.test.js.map +1 -1
  13. package/es/optimize/cleanup.d.ts +8 -0
  14. package/es/optimize/cleanup.js +38 -1
  15. package/es/optimize/cleanup.js.map +1 -1
  16. package/es/utils/__tests__/path-validation.test.d.ts +1 -0
  17. package/es/utils/__tests__/path-validation.test.js +64 -0
  18. package/es/utils/__tests__/path-validation.test.js.map +1 -0
  19. package/es/utils/path-validation.d.ts +29 -0
  20. package/es/utils/path-validation.js +101 -0
  21. package/es/utils/path-validation.js.map +1 -0
  22. package/lib/analyze/paths.js +14 -0
  23. package/lib/analyze/paths.js.map +1 -1
  24. package/lib/compare/__tests__/diff.test.d.ts +1 -0
  25. package/lib/compare/__tests__/diff.test.js +128 -0
  26. package/lib/compare/__tests__/diff.test.js.map +1 -0
  27. package/lib/compare/diff.js +12 -8
  28. package/lib/compare/diff.js.map +1 -1
  29. package/lib/convert/image.js +10 -7
  30. package/lib/convert/image.js.map +1 -1
  31. package/lib/optimize/__tests__/cleanup.test.js +29 -0
  32. package/lib/optimize/__tests__/cleanup.test.js.map +1 -1
  33. package/lib/optimize/cleanup.d.ts +8 -0
  34. package/lib/optimize/cleanup.js +38 -1
  35. package/lib/optimize/cleanup.js.map +1 -1
  36. package/lib/utils/__tests__/path-validation.test.d.ts +1 -0
  37. package/lib/utils/__tests__/path-validation.test.js +69 -0
  38. package/lib/utils/__tests__/path-validation.test.js.map +1 -0
  39. package/lib/utils/path-validation.d.ts +29 -0
  40. package/lib/utils/path-validation.js +109 -0
  41. package/lib/utils/path-validation.js.map +1 -0
  42. package/package.json +10 -3
package/README.md CHANGED
@@ -1,196 +1,214 @@
1
+ <h1 align="center">
2
+ <br/>
3
+ <img width="260" alt="SVG Toolbox Logo" src="https://github.com/user-attachments/assets/fcb0c4f5-8094-44d2-bfca-e4b5f5120f73" />
4
+ <br/>
5
+ </h1>
6
+
1
7
  # SVG Toolbox
2
8
 
3
- 一个功能全面的 SVG 操作和分析工具库,提供 SVG 元素的创建、转换、优化、比较和分析等功能。
9
+ A comprehensive SVG manipulation and analysis library providing capabilities for creating, converting, optimizing, comparing, and analyzing SVG elements.
4
10
 
5
11
  [![npm version](https://img.shields.io/npm/v/svg-toolbox.svg?style=for-the-badge)](https://www.npmjs.com/package/svg-toolbox)
6
12
  [![npm downloads](https://img.shields.io/npm/dy/svg-toolbox.svg?style=for-the-badge)](https://www.npmjs.com/package/svg-toolbox)
7
13
  [![License](https://img.shields.io/github/license/SteamedBread2333/svg-toolbox.svg?style=for-the-badge)](https://www.npmjs.com/package/svg-toolbox)
8
14
 
9
- ## 安装
15
+ ## Installation
10
16
 
11
17
  ```bash
12
18
  npm install svg-toolbox
13
19
  ```
14
20
 
15
- ## 适用场景
21
+ ## Use Cases
22
+
23
+ ### 1. SVG Element Manipulation
16
24
 
17
- ### 1. SVG 元素操作场景
25
+ When you need to create, clone, or merge SVG elements in Node.js environments:
18
26
 
19
- 当你需要在 Node.js 环境中创建、克隆或合并 SVG 元素时,可以使用核心元素操作功能:
27
+ - **Dynamic SVG Generation**: Create SVG elements dynamically based on data
28
+ - **SVG Template Reuse**: Clone existing SVG elements as templates
29
+ - **Combine Multiple SVGs**: Merge multiple independent SVG graphics into one
20
30
 
21
- - **动态生成 SVG 图形**:根据数据动态创建 SVG 元素
22
- - **SVG 模板复用**:克隆现有 SVG 元素作为模板
23
- - **组合多个 SVG**:将多个独立的 SVG 图形合并为一个
31
+ ### 2. Format Conversion
24
32
 
25
- ### 2. 格式转换场景
33
+ When you need to convert SVG to other formats or perform encoding conversions:
26
34
 
27
- 当你需要将 SVG 转换为其他格式或进行编码转换时:
35
+ - **Web Application Embedding**: Convert SVG to Base64 data URI for easy embedding in HTML/CSS
36
+ - **Image Export**: Convert SVG to PNG, JPG, or WebP formats for download or sharing
37
+ - **Cross-platform Compatibility**: Use Base64 encoding when transferring SVG data between different systems
28
38
 
29
- - **Web 应用嵌入**:将 SVG 转换为 Base64 数据 URI,方便嵌入 HTML/CSS
30
- - **图片导出**:将 SVG 转换为 PNG、JPG 或 WebP 格式用于下载或分享
31
- - **跨平台兼容**:在不同系统间传输 SVG 数据时使用 Base64 编码
39
+ ### 3. Visual Regression Testing
32
40
 
33
- ### 3. 视觉回归测试场景
41
+ When you need to compare SVG rendering results to ensure visual consistency:
34
42
 
35
- 当你需要比较 SVG 渲染结果以确保视觉一致性时:
43
+ - **Automated Testing**: Detect SVG rendering changes in CI/CD pipelines
44
+ - **Version Comparison**: Compare differences between different versions of SVG files
45
+ - **Quality Assurance**: Ensure SVG modifications don't introduce unexpected visual changes
36
46
 
37
- - **自动化测试**:在 CI/CD 流程中检测 SVG 渲染变化
38
- - **版本对比**:比较不同版本的 SVG 文件差异
39
- - **质量保证**:确保 SVG 修改不会引入意外的视觉变化
47
+ ### 4. SVG Optimization
40
48
 
41
- ### 4. SVG 优化场景
49
+ When you need to clean and optimize SVG code:
42
50
 
43
- 当你需要清理和优化 SVG 代码时:
51
+ - **Performance Optimization**: Remove invalid coordinates and empty attributes to reduce file size
52
+ - **Code Cleanup**: Remove comments and excess whitespace to improve readability
53
+ - **Data Repair**: Fix path data containing NaN or invalid values
44
54
 
45
- - **性能优化**:移除无效坐标和空属性,减小文件大小
46
- - **代码清理**:移除注释和多余空白,提高可读性
47
- - **数据修复**:修复包含 NaN 或无效值的路径数据
55
+ ### 5. SVG Analysis
48
56
 
49
- ### 5. SVG 分析场景
57
+ When you need to gain insights into SVG content, the analysis features can help you:
50
58
 
51
- 当你需要深入了解 SVG 内容时:
59
+ #### Color Extraction Use Cases
60
+ - **Design System Building**: Batch analyze SVG icon libraries, extract all used colors, and establish color standards for design systems
61
+ - **Theme Adaptation**: Identify colors in SVG and automatically generate dark/light theme versions
62
+ - **Accessibility Checking**: Verify that SVG colors meet WCAG contrast requirements
63
+ - **Brand Consistency Verification**: Verify that SVG icons use brand standard colors
64
+ - **Automated Color Replacement**: Identify colors that need replacement and batch update SVG files
52
65
 
53
- - **设计审查**:提取 SVG 中使用的所有颜色
54
- - **路径分析**:分析路径命令的结构和复杂度
55
- - **尺寸检测**:自动提取 SVG 的尺寸信息
66
+ #### Path Analysis Use Cases
67
+ - **SVG Optimization Decisions**: Analyze path complexity to determine if simplification or optimization is needed
68
+ - **Performance Analysis**: Count path commands to evaluate SVG rendering performance
69
+ - **Format Conversion Preparation**: Understand path structure to prepare for conversion to other formats
70
+ - **Animation Creation**: Analyze path command types to determine suitable animation methods (e.g., path stroke animation)
71
+ - **Quality Detection**: Detect if SVG contains overly complex paths (which may cause performance issues)
72
+ - **Path Editing Tools**: Provide path parsing and editing capabilities for SVG editors
73
+ - **Learning and Teaching**: Analyze SVG path structure to help understand SVG drawing principles
56
74
 
57
- ## 功能模块
75
+ ## Feature Modules
58
76
 
59
- ### 核心功能 (Core)
77
+ ### Core Functions
60
78
 
61
- 提供基础的 SVG 元素操作能力:
79
+ Provides basic SVG element manipulation capabilities:
62
80
 
63
- - `createSVGElement` - 从字符串创建 SVG 元素
64
- - `cloneSVGElement` - 深度克隆 SVG 元素
65
- - `mergeSVGElements` - 合并多个 SVG 元素
66
- - `getSVGDimensions` - 获取 SVG 尺寸信息
81
+ - `createSVGElement` - Create SVG element from string
82
+ - `cloneSVGElement` - Deep clone SVG element
83
+ - `mergeSVGElements` - Merge multiple SVG elements
84
+ - `getSVGDimensions` - Get SVG dimensions
67
85
 
68
- ### 转换功能 (Convert)
86
+ ### Conversion Functions
69
87
 
70
- 提供格式转换和编码功能:
88
+ Provides format conversion and encoding capabilities:
71
89
 
72
- - `convertSVGToBase64` - SVG Base64 编码
73
- - `convertBase64ToSVG` - Base64 解码为 SVG
74
- - `svgToImage` - SVG 转图片格式(PNG/JPG/WebP
75
- - `svg2Png` - SVG PNG(兼容旧版 API
90
+ - `convertSVGToBase64` - Convert SVG to Base64 encoding
91
+ - `convertBase64ToSVG` - Decode Base64 to SVG
92
+ - `svgToImage` - Convert SVG to image formats (PNG/JPG/WebP)
93
+ - `svg2Png` - Convert SVG to PNG (legacy API compatibility)
76
94
 
77
- ### 比较功能 (Compare)
95
+ ### Comparison Functions
78
96
 
79
- 提供图像对比和差异检测:
97
+ Provides image comparison and difference detection:
80
98
 
81
- - `diffImages` - 比较两个图像文件并生成差异图
82
- - `pixelLevelDiff` - 像素级别的图像差异比较
83
- - `diffSvg` - SVG 差异比较(兼容旧版 API
99
+ - `diffImages` - Compare two image files and generate diff image
100
+ - `pixelLevelDiff` - Pixel-level image difference comparison
101
+ - `diffSvg` - SVG difference comparison (legacy API compatibility)
84
102
 
85
- ### 优化功能 (Optimize)
103
+ ### Optimization Functions
86
104
 
87
- 提供 SVG 代码优化和清理:
105
+ Provides SVG code optimization and cleanup:
88
106
 
89
- - `removeNanCoordinates` - 移除路径中的 NaN 坐标
90
- - `removeEmptyAttributes` - 移除空属性
91
- - `removeComments` - 移除注释
92
- - `normalizeWhitespace` - 规范化空白字符
93
- - `optimizeSVG` - 综合优化 SVG 代码
107
+ - `removeNanCoordinates` - Remove NaN coordinates from paths
108
+ - `removeEmptyAttributes` - Remove empty attributes
109
+ - `removeComments` - Remove comments
110
+ - `normalizeWhitespace` - Normalize whitespace characters
111
+ - `optimizeSVG` - Comprehensive SVG code optimization
94
112
 
95
- ### 分析功能 (Analyze)
113
+ ### Analysis Functions
96
114
 
97
- 提供 SVG 内容分析:
115
+ Provides SVG content analysis:
98
116
 
99
- - `extractColors` - 提取 SVG 中使用的颜色
100
- - `parsePathData` - 解析路径数据
101
- - `analyzePaths` - 分析所有路径元素
102
- - `getPathStatistics` - 获取路径统计信息
117
+ - `extractColors` - Extract colors used in SVG
118
+ - `parsePathData` - Parse path data
119
+ - `analyzePaths` - Analyze all path elements
120
+ - `getPathStatistics` - Get path statistics
103
121
 
104
- ## 使用示例
122
+ ## Usage Examples
105
123
 
106
- ### 基础操作
124
+ ### Basic Operations
107
125
 
108
126
  ```typescript
109
127
  import { createSVGElement, cloneSVGElement, mergeSVGElements } from 'svg-toolbox';
110
128
 
111
- // 创建 SVG 元素
129
+ // Create SVG element
112
130
  const svg = createSVGElement('<svg><circle cx="50" cy="50" r="40" /></svg>');
113
131
 
114
- // 克隆元素
132
+ // Clone element
115
133
  const cloned = cloneSVGElement(svg);
116
134
 
117
- // 合并多个元素
135
+ // Merge multiple elements
118
136
  const merged = mergeSVGElements([svg, cloned]);
119
137
  ```
120
138
 
121
- ### 格式转换
139
+ ### Format Conversion
122
140
 
123
141
  ```typescript
124
142
  import { convertSVGToBase64, svgToImage } from 'svg-toolbox';
125
143
 
126
- // 转换为 Base64
144
+ // Convert to Base64
127
145
  const base64 = convertSVGToBase64('<svg>...</svg>');
128
146
 
129
- // 转换为 PNG
147
+ // Convert to PNG
130
148
  const pngBuffer = await svgToImage('input.svg', { scale: 2, format: 'png' });
131
149
 
132
- // 转换为 WebP
150
+ // Convert to WebP
133
151
  const webpBuffer = await svgToImage('input.svg', { format: 'webp', quality: 90 });
134
152
  ```
135
153
 
136
- ### 图像比较
154
+ ### Image Comparison
137
155
 
138
156
  ```typescript
139
157
  import { diffImages } from 'svg-toolbox';
140
158
 
141
- // 比较两个图像并生成差异图
159
+ // Compare two images and generate diff image
142
160
  const result = await diffImages('image1.svg', 'image2.svg', 'diff.png');
143
- console.log(`差异像素数: ${result.numDiffPixels}`);
161
+ console.log(`Number of different pixels: ${result.numDiffPixels}`);
144
162
  ```
145
163
 
146
- ### SVG 优化
164
+ ### SVG Optimization
147
165
 
148
166
  ```typescript
149
167
  import { optimizeSVG, removeNanCoordinates } from 'svg-toolbox';
150
168
 
151
- // 综合优化
169
+ // Comprehensive optimization
152
170
  const optimized = optimizeSVG('<svg><!-- comment --><path d="M 10,20 nan L 30,40" /></svg>');
153
171
 
154
- // 移除 NaN 坐标
172
+ // Remove NaN coordinates
155
173
  const cleaned = removeNanCoordinates('<svg><path d="M 10,20 nan L 30,40" /></svg>');
156
174
  ```
157
175
 
158
- ### 内容分析
176
+ ### Content Analysis
159
177
 
160
178
  ```typescript
161
179
  import { extractColors, getPathStatistics } from 'svg-toolbox';
162
180
 
163
- // 提取颜色
181
+ // Extract colors
164
182
  const colors = extractColors('<svg><circle fill="red" stroke="blue" /></svg>');
165
183
 
166
- // 获取路径统计
184
+ // Get path statistics
167
185
  const stats = getPathStatistics('<svg><path d="M 10,20 L 30,40 Z" /></svg>');
168
- console.log(`路径数: ${stats.totalPaths}, 命令数: ${stats.totalCommands}`);
186
+ console.log(`Paths: ${stats.totalPaths}, Commands: ${stats.totalCommands}`);
169
187
  ```
170
188
 
171
- ## API 文档
189
+ ## API Documentation
172
190
 
173
- 详细的 API 文档请参考 [TypeScript 类型定义](./src/types/index.ts) 和源代码注释。
191
+ For detailed API documentation, please refer to [TypeScript type definitions](./src/types/index.ts) and source code comments.
174
192
 
175
- ## 开发
193
+ ## Development
176
194
 
177
195
  ```bash
178
- # 安装依赖
196
+ # Install dependencies
179
197
  npm install
180
198
 
181
- # 运行测试
199
+ # Run tests
182
200
  npm test
183
201
 
184
- # 构建项目
202
+ # Build project
185
203
  npm run build
186
204
 
187
- # 监听模式测试
205
+ # Watch mode testing
188
206
  npm run test:watch
189
207
 
190
- # 生成覆盖率报告
208
+ # Generate coverage report
191
209
  npm run test:coverage
192
210
  ```
193
211
 
194
- ## 许可证
212
+ ## License
195
213
 
196
214
  MIT License
@@ -4,15 +4,29 @@
4
4
  import { JSDOM } from 'jsdom';
5
5
  import { serializeSVG } from '../core/element';
6
6
  import { isValidSvgString } from '../utils/validation';
7
+ /**
8
+ * Maximum allowed path data length to prevent ReDoS attacks
9
+ */
10
+ const MAX_PATH_DATA_LENGTH = 100000; // 100KB should be sufficient for most SVG paths
7
11
  /**
8
12
  * Parses the 'd' attribute of a path element into command objects
9
13
  */
10
14
  export function parsePathData(pathData) {
15
+ // Validate input length to prevent ReDoS attacks
16
+ if (pathData.length > MAX_PATH_DATA_LENGTH) {
17
+ throw new Error(`Path data length exceeds maximum allowed length of ${MAX_PATH_DATA_LENGTH} characters`);
18
+ }
11
19
  const commands = [];
12
20
  // Split by path commands (M, L, H, V, C, S, Q, T, A, Z)
13
21
  const commandRegex = /([MmLlHhVvCcSsQqTtAaZz])((?:\s*-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\s*,?\s*)*)/g;
14
22
  let match;
23
+ let iterations = 0;
24
+ const MAX_ITERATIONS = 10000; // Prevent infinite loops
15
25
  while ((match = commandRegex.exec(pathData)) !== null) {
26
+ iterations++;
27
+ if (iterations > MAX_ITERATIONS) {
28
+ throw new Error('Path parsing exceeded maximum iterations. Path data may be malformed.');
29
+ }
16
30
  const type = match[1];
17
31
  const paramsStr = match[2].trim();
18
32
  if (type.toLowerCase() === 'z') {
@@ -1 +1 @@
1
- {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/analyze/paths.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/analyze/paths.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAqB,MAAM,qBAAqB,CAAC;AAG1E;;GAEG;AACH,MAAM,oBAAoB,GAAG,MAAM,CAAC,CAAC,gDAAgD;AAErF;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,iDAAiD;IACjD,IAAI,QAAQ,CAAC,MAAM,GAAG,oBAAoB,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,sDAAsD,oBAAoB,aAAa,CAAC,CAAC;IAC3G,CAAC;IAED,MAAM,QAAQ,GAAkB,EAAE,CAAC;IAEnC,wDAAwD;IACxD,MAAM,YAAY,GAAG,6EAA6E,CAAC;IACnG,IAAI,KAAK,CAAC;IACV,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,MAAM,cAAc,GAAG,KAAK,CAAC,CAAC,yBAAyB;IAEvD,OAAO,CAAC,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACtD,UAAU,EAAE,CAAC;QACb,IAAI,UAAU,GAAG,cAAc,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,uEAAuE,CAAC,CAAC;QAC3F,CAAC;QACD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAElC,IAAI,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,EAAE,CAAC;YAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;QACtC,CAAC;aAAM,IAAI,SAAS,EAAE,CAAC;YACrB,MAAM,MAAM,GAAG,SAAS;iBACrB,KAAK,CAAC,QAAQ,CAAC;iBACf,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;iBAC5B,GAAG,CAAC,MAAM,CAAC;iBACX,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAE1B,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,UAA4B;IACvD,MAAM,SAAS,GAAG,gBAAgB,CAAC,UAAU,CAAC;QAC5C,CAAC,CAAC,UAAU;QACZ,CAAC,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;IAE7B,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,SAAS,EAAE;QAC/B,WAAW,EAAE,eAAe;KAC7B,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC;IACrC,MAAM,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAEjD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,KAAK,GAAG,UAAU,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAClD,MAAM,YAAY,GAAG,IAAI,GAAG,EAAyB,CAAC;IAEtD,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QACxC,MAAM,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,CAAC,EAAE,CAAC;YACN,MAAM,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,QAAQ,KAAK,EAAE,CAAC;YACtD,YAAY,CAAC,GAAG,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;QACzC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,UAA4B;IAK5D,MAAM,YAAY,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;IAE9C,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,MAAM,YAAY,GAA2B,EAAE,CAAC;IAEhD,YAAY,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE;QAC9B,aAAa,IAAI,QAAQ,CAAC,MAAM,CAAC;QACjC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;YACrB,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACpC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACrD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,UAAU,EAAE,YAAY,CAAC,IAAI;QAC7B,aAAa;QACb,YAAY;KACb,CAAC;AACJ,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,64 @@
1
+ import { diffImages } from '../diff';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ describe('Image Diff', () => {
5
+ const fixturesDir = path.join(__dirname, '../../../test-fixtures');
6
+ const test1Path = path.join(fixturesDir, 'test1.svg');
7
+ const test2Path = path.join(fixturesDir, 'test2.svg');
8
+ const outputDir = path.join(fixturesDir, 'diff-output');
9
+ const diffImagePath = path.join(outputDir, 'diff-test1-test2.png');
10
+ beforeAll(() => {
11
+ // Ensure output directory exists
12
+ if (!fs.existsSync(outputDir)) {
13
+ fs.mkdirSync(outputDir, { recursive: true });
14
+ }
15
+ });
16
+ afterAll(() => {
17
+ // Clean up: optionally remove the diff image after tests
18
+ // Uncomment the following line if you want to clean up after tests
19
+ // if (fs.existsSync(diffImagePath)) {
20
+ // fs.unlinkSync(diffImagePath);
21
+ // }
22
+ });
23
+ it('should generate diff image between two similar but different SVGs', async () => {
24
+ // Verify test fixtures exist
25
+ expect(fs.existsSync(test1Path)).toBe(true);
26
+ expect(fs.existsSync(test2Path)).toBe(true);
27
+ // Generate diff image
28
+ const result = await diffImages(test1Path, test2Path, diffImagePath, 0.1);
29
+ // Verify diff image was created
30
+ expect(fs.existsSync(diffImagePath)).toBe(true);
31
+ // Verify result contains expected properties
32
+ expect(result).toHaveProperty('diffPngBuffer');
33
+ expect(result).toHaveProperty('numDiffPixels');
34
+ expect(result.diffPngBuffer).toBeInstanceOf(Buffer);
35
+ expect(typeof result.numDiffPixels).toBe('number');
36
+ // Since the SVGs are different, there should be some differences
37
+ expect(result.numDiffPixels).toBeGreaterThan(0);
38
+ // Verify the saved file matches the buffer
39
+ const savedFile = fs.readFileSync(diffImagePath);
40
+ expect(savedFile.equals(result.diffPngBuffer)).toBe(true);
41
+ // Verify file size is reasonable (not empty)
42
+ expect(savedFile.length).toBeGreaterThan(0);
43
+ console.log(`Diff image saved to: ${diffImagePath}`);
44
+ console.log(`Number of different pixels: ${result.numDiffPixels}`);
45
+ });
46
+ it('should handle identical SVGs (no differences)', async () => {
47
+ const identicalDiffPath = path.join(outputDir, 'diff-identical.png');
48
+ // Compare test1 with itself
49
+ const result = await diffImages(test1Path, test1Path, identicalDiffPath, 0.1);
50
+ // Should have no differences
51
+ expect(result.numDiffPixels).toBe(0);
52
+ expect(fs.existsSync(identicalDiffPath)).toBe(true);
53
+ console.log(`Identical comparison diff saved to: ${identicalDiffPath}`);
54
+ console.log(`Number of different pixels: ${result.numDiffPixels}`);
55
+ });
56
+ it('should generate diff without saving to file', async () => {
57
+ const result = await diffImages(test1Path, test2Path, undefined, 0.1);
58
+ // Should still return result even without file path
59
+ expect(result).toHaveProperty('diffPngBuffer');
60
+ expect(result).toHaveProperty('numDiffPixels');
61
+ expect(result.numDiffPixels).toBeGreaterThan(0);
62
+ });
63
+ });
64
+ //# sourceMappingURL=diff.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.test.js","sourceRoot":"","sources":["../../../src/compare/__tests__/diff.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,wBAAwB,CAAC,CAAC;IACnE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IACxD,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,sBAAsB,CAAC,CAAC;IAEnE,SAAS,CAAC,GAAG,EAAE;QACb,iCAAiC;QACjC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9B,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,GAAG,EAAE;QACZ,yDAAyD;QACzD,mEAAmE;QACnE,sCAAsC;QACtC,kCAAkC;QAClC,IAAI;IACN,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,6BAA6B;QAC7B,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE5C,sBAAsB;QACtB,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,CAAC,CAAC;QAE1E,gCAAgC;QAChC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEhD,6CAA6C;QAC7C,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC;QAC/C,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC;QAC/C,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QACpD,MAAM,CAAC,OAAO,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEnD,iEAAiE;QACjE,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAEhD,2CAA2C;QAC3C,MAAM,SAAS,GAAG,EAAE,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC;QACjD,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE1D,6CAA6C;QAC7C,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAE5C,OAAO,CAAC,GAAG,CAAC,wBAAwB,aAAa,EAAE,CAAC,CAAC;QACrD,OAAO,CAAC,GAAG,CAAC,+BAA+B,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;QAC7D,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC;QAErE,4BAA4B;QAC5B,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,SAAS,EAAE,SAAS,EAAE,iBAAiB,EAAE,GAAG,CAAC,CAAC;QAE9E,6BAA6B;QAC7B,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACrC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEpD,OAAO,CAAC,GAAG,CAAC,uCAAuC,iBAAiB,EAAE,CAAC,CAAC;QACxE,OAAO,CAAC,GAAG,CAAC,+BAA+B,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QAC3D,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;QAEtE,oDAAoD;QACpD,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC;QAC/C,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC;QAC/C,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -6,6 +6,7 @@ import path from 'path';
6
6
  import sharp from 'sharp';
7
7
  import { PNG } from 'pngjs';
8
8
  import pixelmatch from 'pixelmatch';
9
+ import { validateReadPath, validateWritePath } from '../utils/path-validation';
9
10
  /**
10
11
  * Compares two PNG images at the pixel level and generates a diff image
11
12
  */
@@ -28,22 +29,27 @@ export function pixelLevelDiff(pngA, pngB, threshold = 0.1) {
28
29
  * Compares two image files (SVG or PNG) and generates a diff image
29
30
  */
30
31
  export async function diffImages(pathA, pathB, diffFilePath, threshold = 0.1) {
31
- if (!fs.existsSync(pathA)) {
32
- throw new Error(`File not found: ${pathA}`);
32
+ // Validate and normalize file paths to prevent path traversal
33
+ const validatedPathA = validateReadPath(pathA, ['.svg', '.png', '.jpg', '.jpeg', '.webp']);
34
+ const validatedPathB = validateReadPath(pathB, ['.svg', '.png', '.jpg', '.jpeg', '.webp']);
35
+ if (!fs.existsSync(validatedPathA)) {
36
+ throw new Error(`File not found: ${validatedPathA}`);
33
37
  }
34
- if (!fs.existsSync(pathB)) {
35
- throw new Error(`File not found: ${pathB}`);
38
+ if (!fs.existsSync(validatedPathB)) {
39
+ throw new Error(`File not found: ${validatedPathB}`);
36
40
  }
37
41
  // Convert both images to PNG buffers
38
- const pngA = await sharp(pathA).png().toBuffer();
39
- const pngB = await sharp(pathB).png().toBuffer();
42
+ const pngA = await sharp(validatedPathA).png().toBuffer();
43
+ const pngB = await sharp(validatedPathB).png().toBuffer();
40
44
  const result = pixelLevelDiff(pngA, pngB, threshold);
41
45
  if (diffFilePath) {
42
46
  const ext = path.extname(diffFilePath);
43
47
  if (!ext) {
44
48
  throw new Error('Diff file path must have a file extension');
45
49
  }
46
- fs.writeFileSync(diffFilePath, result.diffPngBuffer);
50
+ // Validate write path to prevent path traversal
51
+ const validatedWritePath = validateWritePath(diffFilePath, ['.png']);
52
+ fs.writeFileSync(validatedWritePath, result.diffPngBuffer);
47
53
  }
48
54
  return result;
49
55
  }
@@ -1 +1 @@
1
- {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/compare/diff.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/compare/diff.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,7 @@
4
4
  import fs from 'fs';
5
5
  import sharp from 'sharp';
6
6
  import { getSVGDimensions } from '../core/dimensions';
7
+ import { validateReadPath, validateWritePath } from '../utils/path-validation';
7
8
  /**
8
9
  * Converts an SVG file to PNG format
9
10
  *
@@ -13,17 +14,19 @@ import { getSVGDimensions } from '../core/dimensions';
13
14
  */
14
15
  export async function svgToImage(svgPath, options = {}) {
15
16
  const { scale = 2, format = 'png', quality = 90 } = options;
16
- if (!fs.existsSync(svgPath)) {
17
- throw new Error(`SVG file not found: ${svgPath}`);
17
+ // Validate and normalize the file path to prevent path traversal
18
+ const validatedPath = validateReadPath(svgPath, ['.svg']);
19
+ if (!fs.existsSync(validatedPath)) {
20
+ throw new Error(`SVG file not found: ${validatedPath}`);
18
21
  }
19
- const svgContent = fs.readFileSync(svgPath, 'utf8');
22
+ const svgContent = fs.readFileSync(validatedPath, 'utf8');
20
23
  const dimensions = getSVGDimensions(svgContent);
21
24
  if (!dimensions.width || !dimensions.height) {
22
25
  throw new Error('SVG must have width and height or viewBox');
23
26
  }
24
27
  const width = scale * dimensions.width;
25
28
  const height = scale * dimensions.height;
26
- let sharpInstance = sharp(svgPath).resize(width, height);
29
+ let sharpInstance = sharp(validatedPath).resize(width, height);
27
30
  switch (format) {
28
31
  case 'jpg':
29
32
  case 'jpeg':
@@ -50,7 +53,9 @@ export async function svg2Png(svgPath, pngPath, scale) {
50
53
  }
51
54
  const buffer = await svgToImage(svgPath, { scale: scale ?? 2, format: 'png' });
52
55
  if (pngPath) {
53
- fs.writeFileSync(pngPath, buffer);
56
+ // Validate write path to prevent path traversal
57
+ const validatedWritePath = validateWritePath(pngPath, ['.png']);
58
+ fs.writeFileSync(validatedWritePath, buffer);
54
59
  return;
55
60
  }
56
61
  return buffer;
@@ -1 +1 @@
1
- {"version":3,"file":"image.js","sourceRoot":"","sources":["../../src/convert/image.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGtD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAe,EACf,UAA6B,EAAE;IAE/B,MAAM,EAAE,KAAK,GAAG,CAAC,EAAE,MAAM,GAAG,KAAK,EAAE,OAAO,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC;IAE5D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,uBAAuB,OAAO,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,UAAU,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACpD,MAAM,UAAU,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAEhD,IAAI,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC/D,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC;IACvC,MAAM,MAAM,GAAG,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC;IAEzC,IAAI,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAEzD,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,KAAK,CAAC;QACX,KAAK,MAAM;YACT,aAAa,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;YAChD,MAAM;QACR,KAAK,MAAM;YACT,aAAa,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;YAChD,MAAM;QACR,KAAK,KAAK,CAAC;QACX;YACE,aAAa,GAAG,aAAa,CAAC,GAAG,EAAE,CAAC;YACpC,MAAM;IACV,CAAC;IAED,OAAO,aAAa,CAAC,QAAQ,EAAE,CAAC;AAClC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,OAAe,EACf,OAAgB,EAChB,KAAc;IAEd,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAE/E,IAAI,OAAO,EAAE,CAAC;QACZ,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAClC,OAAO;IACT,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"image.js","sourceRoot":"","sources":["../../src/convert/image.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE/E;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAe,EACf,UAA6B,EAAE;IAE/B,MAAM,EAAE,KAAK,GAAG,CAAC,EAAE,MAAM,GAAG,KAAK,EAAE,OAAO,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC;IAE5D,iEAAiE;IACjE,MAAM,aAAa,GAAG,gBAAgB,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAE1D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,uBAAuB,aAAa,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,UAAU,GAAG,EAAE,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAEhD,IAAI,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC/D,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC;IACvC,MAAM,MAAM,GAAG,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC;IAEzC,IAAI,aAAa,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAE/D,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,KAAK,CAAC;QACX,KAAK,MAAM;YACT,aAAa,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;YAChD,MAAM;QACR,KAAK,MAAM;YACT,aAAa,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;YAChD,MAAM;QACR,KAAK,KAAK,CAAC;QACX;YACE,aAAa,GAAG,aAAa,CAAC,GAAG,EAAE,CAAC;YACpC,MAAM;IACV,CAAC;IAED,OAAO,aAAa,CAAC,QAAQ,EAAE,CAAC;AAClC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,OAAe,EACf,OAAgB,EAChB,KAAc;IAEd,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAE/E,IAAI,OAAO,EAAE,CAAC;QACZ,gDAAgD;QAChD,MAAM,kBAAkB,GAAG,iBAAiB,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;QAChE,EAAE,CAAC,aAAa,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;QAC7C,OAAO;IACT,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -31,6 +31,35 @@ describe('SVG Cleanup', () => {
31
31
  const result = removeComments(svgContent);
32
32
  expect(result).not.toContain('Comment');
33
33
  });
34
+ it('should handle nested comments correctly', () => {
35
+ // This tests the incomplete sanitization fix
36
+ // Input: "<!--<!-- comment -->-->" should become "" not "<!-- comment -->"
37
+ const svgContent = '<svg><!--<!-- nested comment -->--><circle /></svg>';
38
+ const result = removeComments(svgContent);
39
+ expect(result).not.toContain('<!--');
40
+ expect(result).not.toContain('-->');
41
+ expect(result).not.toContain('nested comment');
42
+ expect(result).toContain('circle');
43
+ });
44
+ it('should handle deeply nested comments', () => {
45
+ const svgContent = '<svg><!--<!--<!-- triple nested -->-->--><circle /></svg>';
46
+ const result = removeComments(svgContent);
47
+ expect(result).not.toContain('<!--');
48
+ expect(result).not.toContain('-->');
49
+ expect(result).toContain('circle');
50
+ });
51
+ it('should throw error for content exceeding maximum length', () => {
52
+ const largeContent = '<!--' + 'x'.repeat(10000001) + '-->';
53
+ expect(() => removeComments(largeContent)).toThrow('exceeds maximum allowed length');
54
+ });
55
+ it('should handle comments with newlines', () => {
56
+ const svgContent = '<svg><!--\nMulti-line\ncomment\n--><circle /></svg>';
57
+ const result = removeComments(svgContent);
58
+ expect(result).not.toContain('<!--');
59
+ expect(result).not.toContain('-->');
60
+ expect(result).not.toContain('Multi-line');
61
+ expect(result).toContain('circle');
62
+ });
34
63
  });
35
64
  describe('normalizeWhitespace', () => {
36
65
  it('should normalize whitespace', () => {
@@ -1 +1 @@
1
- {"version":3,"file":"cleanup.test.js","sourceRoot":"","sources":["../../../src/optimize/__tests__/cleanup.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cleanup.test.js","sourceRoot":"","sources":["../../../src/optimize/__tests__/cleanup.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACrG,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;QACrC,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;YACxC,MAAM,UAAU,GAAG,iEAAiE,CAAC;YACrF,MAAM,MAAM,GAAG,qBAAqB,CAAC,UAAU,CAAC,CAAC;YAEjD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;YACpC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;YACvC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;YAC5C,MAAM,UAAU,GAAG,yDAAyD,CAAC;YAC7E,MAAM,OAAO,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;YAC7C,MAAM,MAAM,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;YAE9C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YACnC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC9B,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;YACrC,MAAM,UAAU,GAAG,wEAAwE,CAAC;YAC5F,MAAM,MAAM,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;YAE1C,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACpC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;YACzC,MAAM,UAAU,GAAG,2DAA2D,CAAC;YAC/E,MAAM,MAAM,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;YAE1C,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;YACjD,6CAA6C;YAC7C,2EAA2E;YAC3E,MAAM,UAAU,GAAG,qDAAqD,CAAC;YACzE,MAAM,MAAM,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;YAE1C,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACpC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;YAC/C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;YAC9C,MAAM,UAAU,GAAG,2DAA2D,CAAC;YAC/E,MAAM,MAAM,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;YAE1C,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACpC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;YACjE,MAAM,YAAY,GAAG,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC;YAC3D,MAAM,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,gCAAgC,CAAC,CAAC;QACvF,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;YAC9C,MAAM,UAAU,GAAG,qDAAqD,CAAC;YACzE,MAAM,MAAM,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;YAE1C,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACpC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;YAC3C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;QACnC,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;YACrC,MAAM,UAAU,GAAG,mDAAmD,CAAC;YACvE,MAAM,MAAM,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;YAE/C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;YACpC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;YAC/C,MAAM,UAAU,GAAG,yCAAyC,CAAC;YAC7D,MAAM,MAAM,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;YAE/C,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YACnC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;QAC3B,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;YACxC,MAAM,UAAU,GAAG,0EAA0E,CAAC;YAC9F,MAAM,MAAM,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;YAEvC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YACtC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YACnC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;YAC5C,MAAM,UAAU,GAAG,+CAA+C,CAAC;YACnE,MAAM,OAAO,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;YAC7C,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;YAEpC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -7,6 +7,14 @@
7
7
  export declare function removeEmptyAttributes(svgContent: Element | string): string;
8
8
  /**
9
9
  * Removes comments from SVG content
10
+ *
11
+ * This function uses iterative replacement to handle nested comments correctly
12
+ * and includes input length validation to prevent ReDoS attacks.
13
+ *
14
+ * The function handles cases like:
15
+ * - Simple comments: <!-- comment -->
16
+ * - Nested comments: <!--<!-- inner -->-->
17
+ * - Multiple comments: <!-- one --><!-- two -->
10
18
  */
11
19
  export declare function removeComments(svgContent: string): string;
12
20
  /**