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
@@ -33,11 +33,48 @@ export function removeEmptyAttributes(svgContent) {
33
33
  removeEmptyAttrs(svgElement);
34
34
  return svgElement.outerHTML.trim();
35
35
  }
36
+ /**
37
+ * Maximum allowed SVG content length to prevent ReDoS attacks
38
+ */
39
+ const MAX_SVG_CONTENT_LENGTH = 10000000; // 10MB should be sufficient for most SVG files
36
40
  /**
37
41
  * Removes comments from SVG content
42
+ *
43
+ * This function uses iterative replacement to handle nested comments correctly
44
+ * and includes input length validation to prevent ReDoS attacks.
45
+ *
46
+ * The function handles cases like:
47
+ * - Simple comments: <!-- comment -->
48
+ * - Nested comments: <!--<!-- inner -->-->
49
+ * - Multiple comments: <!-- one --><!-- two -->
38
50
  */
39
51
  export function removeComments(svgContent) {
40
- return svgContent.replace(/<!--[\s\S]*?-->/g, '');
52
+ // Validate input length to prevent ReDoS attacks
53
+ if (svgContent.length > MAX_SVG_CONTENT_LENGTH) {
54
+ throw new Error(`SVG content length exceeds maximum allowed length of ${MAX_SVG_CONTENT_LENGTH} characters`);
55
+ }
56
+ // Use iterative replacement to handle nested comments
57
+ // This prevents incomplete sanitization where nested comments could reappear
58
+ // Example: "<!--<!-- comment -->-->" should become "" not "<!-- comment -->"
59
+ let previous;
60
+ let result = svgContent;
61
+ let iterations = 0;
62
+ const MAX_ITERATIONS = 1000; // Prevent infinite loops
63
+ do {
64
+ previous = result;
65
+ // Match HTML comments and standalone delimiters:
66
+ // - Full comments: <!-- ... --> or <!-- ... --!>
67
+ // - Orphaned starts: <!--
68
+ // - Orphaned ends: --> or --!>
69
+ // The regex uses non-greedy matching (*?) to match the shortest possible comment
70
+ // and is applied iteratively until no further matches remain.
71
+ result = result.replace(/<!--[\s\S]*?--!?>|<!--|--!?>(?=[^>]|$)/g, '');
72
+ iterations++;
73
+ if (iterations > MAX_ITERATIONS) {
74
+ throw new Error('Comment removal exceeded maximum iterations. SVG content may be malformed.');
75
+ }
76
+ } while (result !== previous);
77
+ return result;
41
78
  }
42
79
  /**
43
80
  * Normalizes whitespace in SVG content
@@ -1 +1 @@
1
- {"version":3,"file":"cleanup.js","sourceRoot":"","sources":["../../src/optimize/cleanup.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;AAE1E;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,UAA4B;IAChE,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,SAAS,gBAAgB,CAAC,OAAgB;QACxC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC7C,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;YACnB,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;gBAC5C,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrC,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;YAC3C,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAE7B,OAAO,UAAU,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;AACrC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,UAAkB;IAC/C,OAAO,UAAU,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;AACpD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAAkB;IACpD,OAAO,UAAU;SACd,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC;SACvB,IAAI,EAAE,CAAC;AACZ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,UAA4B;IACtD,IAAI,SAAS,GAAG,gBAAgB,CAAC,UAAU,CAAC;QAC1C,CAAC,CAAC,UAAU;QACZ,CAAC,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;IAE7B,SAAS,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;IACtC,SAAS,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAC7C,SAAS,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;IAE3C,OAAO,SAAS,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"cleanup.js","sourceRoot":"","sources":["../../src/optimize/cleanup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,64 @@
1
+ import { validateFilePath, validateReadPath, validateWritePath } from '../path-validation';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import os from 'os';
5
+ describe('Path Validation', () => {
6
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'svg-toolbox-test-'));
7
+ afterAll(() => {
8
+ fs.rmSync(tempDir, { recursive: true, force: true });
9
+ });
10
+ describe('validateFilePath', () => {
11
+ it('should normalize valid paths', () => {
12
+ const result = validateFilePath('./test.svg');
13
+ expect(path.isAbsolute(result)).toBe(true);
14
+ });
15
+ it('should reject paths with .. traversal', () => {
16
+ expect(() => validateFilePath('../etc/passwd')).toThrow('Path traversal detected');
17
+ expect(() => validateFilePath('../../secret')).toThrow('Path traversal detected');
18
+ });
19
+ it('should reject empty paths', () => {
20
+ expect(() => validateFilePath('')).toThrow('File path must be a non-empty string');
21
+ expect(() => validateFilePath(null)).toThrow('File path must be a non-empty string');
22
+ });
23
+ it('should restrict paths to base directory when provided', () => {
24
+ const baseDir = tempDir;
25
+ const validPath = path.join(baseDir, 'test.svg');
26
+ expect(() => validateFilePath(validPath, baseDir)).not.toThrow();
27
+ expect(() => validateFilePath('/etc/passwd', baseDir)).toThrow('Path is outside the allowed directory');
28
+ });
29
+ });
30
+ describe('validateReadPath', () => {
31
+ it('should validate file extensions', () => {
32
+ expect(() => validateReadPath('test.svg', ['.svg'])).not.toThrow();
33
+ expect(() => validateReadPath('test.png', ['.svg'])).toThrow('File extension .png is not allowed');
34
+ });
35
+ it('should accept multiple allowed extensions', () => {
36
+ expect(() => validateReadPath('test.svg', ['.svg', '.png'])).not.toThrow();
37
+ expect(() => validateReadPath('test.png', ['.svg', '.png'])).not.toThrow();
38
+ });
39
+ it('should be case-insensitive for extensions', () => {
40
+ expect(() => validateReadPath('test.SVG', ['.svg'])).not.toThrow();
41
+ });
42
+ });
43
+ describe('validateWritePath', () => {
44
+ it('should validate file extensions', () => {
45
+ const writePath = path.join(tempDir, 'test.png');
46
+ expect(() => validateWritePath(writePath, ['.png'])).not.toThrow();
47
+ expect(() => validateWritePath(writePath, ['.svg'])).toThrow('File extension .png is not allowed');
48
+ });
49
+ it('should create directory if it does not exist', () => {
50
+ const newDir = path.join(tempDir, 'new-dir');
51
+ const writePath = path.join(newDir, 'test.png');
52
+ expect(fs.existsSync(newDir)).toBe(false);
53
+ validateWritePath(writePath, ['.png']);
54
+ expect(fs.existsSync(newDir)).toBe(true);
55
+ });
56
+ it('should restrict writes to base directory when provided', () => {
57
+ const baseDir = tempDir;
58
+ const validPath = path.join(baseDir, 'test.png');
59
+ expect(() => validateWritePath(validPath, ['.png'], baseDir)).not.toThrow();
60
+ expect(() => validateWritePath('/tmp/test.png', ['.png'], baseDir)).toThrow('Path is outside the allowed directory');
61
+ });
62
+ });
63
+ });
64
+ //# sourceMappingURL=path-validation.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-validation.test.js","sourceRoot":"","sources":["../../../src/utils/__tests__/path-validation.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Path validation utilities to prevent path traversal attacks
3
+ */
4
+ /**
5
+ * Validates and normalizes a file path to prevent directory traversal attacks
6
+ *
7
+ * @param filePath - The file path to validate
8
+ * @param baseDirectory - Optional base directory to restrict paths to
9
+ * @returns Normalized absolute path
10
+ * @throws Error if path is invalid or contains traversal sequences
11
+ */
12
+ export declare function validateFilePath(filePath: string, baseDirectory?: string): string;
13
+ /**
14
+ * Validates that a file path is safe for reading
15
+ *
16
+ * @param filePath - The file path to validate
17
+ * @param allowedExtensions - Optional array of allowed file extensions (e.g., ['.svg', '.png'])
18
+ * @returns Normalized absolute path
19
+ */
20
+ export declare function validateReadPath(filePath: string, allowedExtensions?: string[]): string;
21
+ /**
22
+ * Validates that a file path is safe for writing
23
+ *
24
+ * @param filePath - The file path to validate
25
+ * @param allowedExtensions - Optional array of allowed file extensions
26
+ * @param baseDirectory - Optional base directory to restrict writes to
27
+ * @returns Normalized absolute path
28
+ */
29
+ export declare function validateWritePath(filePath: string, allowedExtensions?: string[], baseDirectory?: string): string;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Path validation utilities to prevent path traversal attacks
3
+ */
4
+ import path from 'path';
5
+ import fs from 'fs';
6
+ /**
7
+ * Maximum allowed path length to prevent buffer overflow attacks
8
+ */
9
+ const MAX_PATH_LENGTH = 4096;
10
+ /**
11
+ * Validates and normalizes a file path to prevent directory traversal attacks
12
+ *
13
+ * @param filePath - The file path to validate
14
+ * @param baseDirectory - Optional base directory to restrict paths to
15
+ * @returns Normalized absolute path
16
+ * @throws Error if path is invalid or contains traversal sequences
17
+ */
18
+ export function validateFilePath(filePath, baseDirectory) {
19
+ if (!filePath || typeof filePath !== 'string') {
20
+ throw new Error('File path must be a non-empty string');
21
+ }
22
+ // Check path length to prevent buffer overflow attacks
23
+ if (filePath.length > MAX_PATH_LENGTH) {
24
+ throw new Error(`Path length exceeds maximum allowed length of ${MAX_PATH_LENGTH} characters`);
25
+ }
26
+ // Check for null bytes which can be used in path traversal attacks
27
+ if (filePath.includes('\0')) {
28
+ throw new Error('Path contains null bytes which are not allowed');
29
+ }
30
+ // Check for path traversal sequences BEFORE normalization
31
+ // This catches attempts like ../, ..\, etc.
32
+ if (filePath.includes('..')) {
33
+ throw new Error('Path traversal detected: paths containing ".." are not allowed');
34
+ }
35
+ // Resolve to absolute path first, then normalize
36
+ // This ensures that . sequences are resolved
37
+ const absolutePath = path.isAbsolute(filePath)
38
+ ? path.resolve(filePath)
39
+ : path.resolve(process.cwd(), filePath);
40
+ // Normalize the resolved path
41
+ const normalizedPath = path.normalize(absolutePath);
42
+ // Additional safety check: ensure the normalized path doesn't contain .. after resolution
43
+ if (normalizedPath.includes('..')) {
44
+ throw new Error('Path traversal detected: paths containing ".." are not allowed');
45
+ }
46
+ // If base directory is provided, ensure the path is within it
47
+ if (baseDirectory) {
48
+ const baseDir = path.isAbsolute(baseDirectory)
49
+ ? path.resolve(path.normalize(baseDirectory))
50
+ : path.resolve(process.cwd(), baseDirectory);
51
+ const resolvedPath = path.resolve(normalizedPath);
52
+ // Use path.relative to check if path is within base directory
53
+ const relativePath = path.relative(baseDir, resolvedPath);
54
+ // If relative path starts with .. or is absolute, it's outside the base directory
55
+ if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
56
+ throw new Error('Path is outside the allowed directory');
57
+ }
58
+ }
59
+ return normalizedPath;
60
+ }
61
+ /**
62
+ * Validates that a file path is safe for reading
63
+ *
64
+ * @param filePath - The file path to validate
65
+ * @param allowedExtensions - Optional array of allowed file extensions (e.g., ['.svg', '.png'])
66
+ * @returns Normalized absolute path
67
+ */
68
+ export function validateReadPath(filePath, allowedExtensions) {
69
+ const validatedPath = validateFilePath(filePath);
70
+ if (allowedExtensions && allowedExtensions.length > 0) {
71
+ const ext = path.extname(validatedPath).toLowerCase();
72
+ if (!allowedExtensions.includes(ext)) {
73
+ throw new Error(`File extension ${ext} is not allowed. Allowed extensions: ${allowedExtensions.join(', ')}`);
74
+ }
75
+ }
76
+ return validatedPath;
77
+ }
78
+ /**
79
+ * Validates that a file path is safe for writing
80
+ *
81
+ * @param filePath - The file path to validate
82
+ * @param allowedExtensions - Optional array of allowed file extensions
83
+ * @param baseDirectory - Optional base directory to restrict writes to
84
+ * @returns Normalized absolute path
85
+ */
86
+ export function validateWritePath(filePath, allowedExtensions, baseDirectory) {
87
+ const validatedPath = validateFilePath(filePath, baseDirectory);
88
+ if (allowedExtensions && allowedExtensions.length > 0) {
89
+ const ext = path.extname(validatedPath).toLowerCase();
90
+ if (!allowedExtensions.includes(ext)) {
91
+ throw new Error(`File extension ${ext} is not allowed. Allowed extensions: ${allowedExtensions.join(', ')}`);
92
+ }
93
+ }
94
+ // Ensure the directory exists
95
+ const dir = path.dirname(validatedPath);
96
+ if (!fs.existsSync(dir)) {
97
+ fs.mkdirSync(dir, { recursive: true });
98
+ }
99
+ return validatedPath;
100
+ }
101
+ //# sourceMappingURL=path-validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-validation.js","sourceRoot":"","sources":["../../src/utils/path-validation.ts"],"names":[],"mappings":"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"}
@@ -9,15 +9,29 @@ exports.getPathStatistics = getPathStatistics;
9
9
  var jsdom_1 = require("jsdom");
10
10
  var element_1 = require("../core/element");
11
11
  var validation_1 = require("../utils/validation");
12
+ /**
13
+ * Maximum allowed path data length to prevent ReDoS attacks
14
+ */
15
+ var MAX_PATH_DATA_LENGTH = 100000; // 100KB should be sufficient for most SVG paths
12
16
  /**
13
17
  * Parses the 'd' attribute of a path element into command objects
14
18
  */
15
19
  function parsePathData(pathData) {
20
+ // Validate input length to prevent ReDoS attacks
21
+ if (pathData.length > MAX_PATH_DATA_LENGTH) {
22
+ throw new Error("Path data length exceeds maximum allowed length of ".concat(MAX_PATH_DATA_LENGTH, " characters"));
23
+ }
16
24
  var commands = [];
17
25
  // Split by path commands (M, L, H, V, C, S, Q, T, A, Z)
18
26
  var commandRegex = /([MmLlHhVvCcSsQqTtAaZz])((?:\s*-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\s*,?\s*)*)/g;
19
27
  var match;
28
+ var iterations = 0;
29
+ var MAX_ITERATIONS = 10000; // Prevent infinite loops
20
30
  while ((match = commandRegex.exec(pathData)) !== null) {
31
+ iterations++;
32
+ if (iterations > MAX_ITERATIONS) {
33
+ throw new Error('Path parsing exceeded maximum iterations. Path data may be malformed.');
34
+ }
21
35
  var type = match[1];
22
36
  var paramsStr = match[2].trim();
23
37
  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":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ var __importDefault = (this && this.__importDefault) || function (mod) {
39
+ return (mod && mod.__esModule) ? mod : { "default": mod };
40
+ };
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ var diff_1 = require("../diff");
43
+ var fs_1 = __importDefault(require("fs"));
44
+ var path_1 = __importDefault(require("path"));
45
+ describe('Image Diff', function () {
46
+ var fixturesDir = path_1.default.join(__dirname, '../../../test-fixtures');
47
+ var test1Path = path_1.default.join(fixturesDir, 'test1.svg');
48
+ var test2Path = path_1.default.join(fixturesDir, 'test2.svg');
49
+ var outputDir = path_1.default.join(fixturesDir, 'diff-output');
50
+ var diffImagePath = path_1.default.join(outputDir, 'diff-test1-test2.png');
51
+ beforeAll(function () {
52
+ // Ensure output directory exists
53
+ if (!fs_1.default.existsSync(outputDir)) {
54
+ fs_1.default.mkdirSync(outputDir, { recursive: true });
55
+ }
56
+ });
57
+ afterAll(function () {
58
+ // Clean up: optionally remove the diff image after tests
59
+ // Uncomment the following line if you want to clean up after tests
60
+ // if (fs.existsSync(diffImagePath)) {
61
+ // fs.unlinkSync(diffImagePath);
62
+ // }
63
+ });
64
+ it('should generate diff image between two similar but different SVGs', function () { return __awaiter(void 0, void 0, void 0, function () {
65
+ var result, savedFile;
66
+ return __generator(this, function (_a) {
67
+ switch (_a.label) {
68
+ case 0:
69
+ // Verify test fixtures exist
70
+ expect(fs_1.default.existsSync(test1Path)).toBe(true);
71
+ expect(fs_1.default.existsSync(test2Path)).toBe(true);
72
+ return [4 /*yield*/, (0, diff_1.diffImages)(test1Path, test2Path, diffImagePath, 0.1)];
73
+ case 1:
74
+ result = _a.sent();
75
+ // Verify diff image was created
76
+ expect(fs_1.default.existsSync(diffImagePath)).toBe(true);
77
+ // Verify result contains expected properties
78
+ expect(result).toHaveProperty('diffPngBuffer');
79
+ expect(result).toHaveProperty('numDiffPixels');
80
+ expect(result.diffPngBuffer).toBeInstanceOf(Buffer);
81
+ expect(typeof result.numDiffPixels).toBe('number');
82
+ // Since the SVGs are different, there should be some differences
83
+ expect(result.numDiffPixels).toBeGreaterThan(0);
84
+ savedFile = fs_1.default.readFileSync(diffImagePath);
85
+ expect(savedFile.equals(result.diffPngBuffer)).toBe(true);
86
+ // Verify file size is reasonable (not empty)
87
+ expect(savedFile.length).toBeGreaterThan(0);
88
+ console.log("Diff image saved to: ".concat(diffImagePath));
89
+ console.log("Number of different pixels: ".concat(result.numDiffPixels));
90
+ return [2 /*return*/];
91
+ }
92
+ });
93
+ }); });
94
+ it('should handle identical SVGs (no differences)', function () { return __awaiter(void 0, void 0, void 0, function () {
95
+ var identicalDiffPath, result;
96
+ return __generator(this, function (_a) {
97
+ switch (_a.label) {
98
+ case 0:
99
+ identicalDiffPath = path_1.default.join(outputDir, 'diff-identical.png');
100
+ return [4 /*yield*/, (0, diff_1.diffImages)(test1Path, test1Path, identicalDiffPath, 0.1)];
101
+ case 1:
102
+ result = _a.sent();
103
+ // Should have no differences
104
+ expect(result.numDiffPixels).toBe(0);
105
+ expect(fs_1.default.existsSync(identicalDiffPath)).toBe(true);
106
+ console.log("Identical comparison diff saved to: ".concat(identicalDiffPath));
107
+ console.log("Number of different pixels: ".concat(result.numDiffPixels));
108
+ return [2 /*return*/];
109
+ }
110
+ });
111
+ }); });
112
+ it('should generate diff without saving to file', function () { return __awaiter(void 0, void 0, void 0, function () {
113
+ var result;
114
+ return __generator(this, function (_a) {
115
+ switch (_a.label) {
116
+ case 0: return [4 /*yield*/, (0, diff_1.diffImages)(test1Path, test2Path, undefined, 0.1)];
117
+ case 1:
118
+ result = _a.sent();
119
+ // Should still return result even without file path
120
+ expect(result).toHaveProperty('diffPngBuffer');
121
+ expect(result).toHaveProperty('numDiffPixels');
122
+ expect(result.numDiffPixels).toBeGreaterThan(0);
123
+ return [2 /*return*/];
124
+ }
125
+ });
126
+ }); });
127
+ });
128
+ //# 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":"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"}
@@ -50,6 +50,7 @@ var path_1 = __importDefault(require("path"));
50
50
  var sharp_1 = __importDefault(require("sharp"));
51
51
  var pngjs_1 = require("pngjs");
52
52
  var pixelmatch_1 = __importDefault(require("pixelmatch"));
53
+ var path_validation_1 = require("../utils/path-validation");
53
54
  /**
54
55
  * Compares two PNG images at the pixel level and generates a diff image
55
56
  */
@@ -74,21 +75,23 @@ function pixelLevelDiff(pngA, pngB, threshold) {
74
75
  */
75
76
  function diffImages(pathA_1, pathB_1, diffFilePath_1) {
76
77
  return __awaiter(this, arguments, void 0, function (pathA, pathB, diffFilePath, threshold) {
77
- var pngA, pngB, result, ext;
78
+ var validatedPathA, validatedPathB, pngA, pngB, result, ext, validatedWritePath;
78
79
  if (threshold === void 0) { threshold = 0.1; }
79
80
  return __generator(this, function (_a) {
80
81
  switch (_a.label) {
81
82
  case 0:
82
- if (!fs_1.default.existsSync(pathA)) {
83
- throw new Error("File not found: ".concat(pathA));
83
+ validatedPathA = (0, path_validation_1.validateReadPath)(pathA, ['.svg', '.png', '.jpg', '.jpeg', '.webp']);
84
+ validatedPathB = (0, path_validation_1.validateReadPath)(pathB, ['.svg', '.png', '.jpg', '.jpeg', '.webp']);
85
+ if (!fs_1.default.existsSync(validatedPathA)) {
86
+ throw new Error("File not found: ".concat(validatedPathA));
84
87
  }
85
- if (!fs_1.default.existsSync(pathB)) {
86
- throw new Error("File not found: ".concat(pathB));
88
+ if (!fs_1.default.existsSync(validatedPathB)) {
89
+ throw new Error("File not found: ".concat(validatedPathB));
87
90
  }
88
- return [4 /*yield*/, (0, sharp_1.default)(pathA).png().toBuffer()];
91
+ return [4 /*yield*/, (0, sharp_1.default)(validatedPathA).png().toBuffer()];
89
92
  case 1:
90
93
  pngA = _a.sent();
91
- return [4 /*yield*/, (0, sharp_1.default)(pathB).png().toBuffer()];
94
+ return [4 /*yield*/, (0, sharp_1.default)(validatedPathB).png().toBuffer()];
92
95
  case 2:
93
96
  pngB = _a.sent();
94
97
  result = pixelLevelDiff(pngA, pngB, threshold);
@@ -97,7 +100,8 @@ function diffImages(pathA_1, pathB_1, diffFilePath_1) {
97
100
  if (!ext) {
98
101
  throw new Error('Diff file path must have a file extension');
99
102
  }
100
- fs_1.default.writeFileSync(diffFilePath, result.diffPngBuffer);
103
+ validatedWritePath = (0, path_validation_1.validateWritePath)(diffFilePath, ['.png']);
104
+ fs_1.default.writeFileSync(validatedWritePath, result.diffPngBuffer);
101
105
  }
102
106
  return [2 /*return*/, result];
103
107
  }
@@ -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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"}
@@ -47,6 +47,7 @@ exports.svg2Png = svg2Png;
47
47
  var fs_1 = __importDefault(require("fs"));
48
48
  var sharp_1 = __importDefault(require("sharp"));
49
49
  var dimensions_1 = require("../core/dimensions");
50
+ var path_validation_1 = require("../utils/path-validation");
50
51
  /**
51
52
  * Converts an SVG file to PNG format
52
53
  *
@@ -56,21 +57,22 @@ var dimensions_1 = require("../core/dimensions");
56
57
  */
57
58
  function svgToImage(svgPath_1) {
58
59
  return __awaiter(this, arguments, void 0, function (svgPath, options) {
59
- var _a, scale, _b, format, _c, quality, svgContent, dimensions, width, height, sharpInstance;
60
+ var _a, scale, _b, format, _c, quality, validatedPath, svgContent, dimensions, width, height, sharpInstance;
60
61
  if (options === void 0) { options = {}; }
61
62
  return __generator(this, function (_d) {
62
63
  _a = options.scale, scale = _a === void 0 ? 2 : _a, _b = options.format, format = _b === void 0 ? 'png' : _b, _c = options.quality, quality = _c === void 0 ? 90 : _c;
63
- if (!fs_1.default.existsSync(svgPath)) {
64
- throw new Error("SVG file not found: ".concat(svgPath));
64
+ validatedPath = (0, path_validation_1.validateReadPath)(svgPath, ['.svg']);
65
+ if (!fs_1.default.existsSync(validatedPath)) {
66
+ throw new Error("SVG file not found: ".concat(validatedPath));
65
67
  }
66
- svgContent = fs_1.default.readFileSync(svgPath, 'utf8');
68
+ svgContent = fs_1.default.readFileSync(validatedPath, 'utf8');
67
69
  dimensions = (0, dimensions_1.getSVGDimensions)(svgContent);
68
70
  if (!dimensions.width || !dimensions.height) {
69
71
  throw new Error('SVG must have width and height or viewBox');
70
72
  }
71
73
  width = scale * dimensions.width;
72
74
  height = scale * dimensions.height;
73
- sharpInstance = (0, sharp_1.default)(svgPath).resize(width, height);
75
+ sharpInstance = (0, sharp_1.default)(validatedPath).resize(width, height);
74
76
  switch (format) {
75
77
  case 'jpg':
76
78
  case 'jpeg':
@@ -95,7 +97,7 @@ function svgToImage(svgPath_1) {
95
97
  */
96
98
  function svg2Png(svgPath, pngPath, scale) {
97
99
  return __awaiter(this, void 0, void 0, function () {
98
- var buffer;
100
+ var buffer, validatedWritePath;
99
101
  return __generator(this, function (_a) {
100
102
  switch (_a.label) {
101
103
  case 0:
@@ -106,7 +108,8 @@ function svg2Png(svgPath, pngPath, scale) {
106
108
  case 1:
107
109
  buffer = _a.sent();
108
110
  if (pngPath) {
109
- fs_1.default.writeFileSync(pngPath, buffer);
111
+ validatedWritePath = (0, path_validation_1.validateWritePath)(pngPath, ['.png']);
112
+ fs_1.default.writeFileSync(validatedWritePath, buffer);
110
113
  return [2 /*return*/];
111
114
  }
112
115
  return [2 /*return*/, buffer];
@@ -1 +1 @@
1
- {"version":3,"file":"image.js","sourceRoot":"","sources":["../../src/convert/image.ts"],"names":[],"mappings":";AAAA;;GAEG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAcH,gCAqCC;AAOD,0BAiBC;AAzED,0CAAoB;AACpB,gDAA0B;AAC1B,iDAAsD;AAGtD;;;;;;GAMG;AACH,SAAsB,UAAU;wDAC9B,OAAe,EACf,OAA+B;;QAA/B,wBAAA,EAAA,YAA+B;;YAEvB,KAA4C,OAAO,MAA1C,EAAT,KAAK,mBAAG,CAAC,KAAA,EAAE,KAAiC,OAAO,OAA1B,EAAd,MAAM,mBAAG,KAAK,KAAA,EAAE,KAAiB,OAAO,QAAZ,EAAZ,OAAO,mBAAG,EAAE,KAAA,CAAa;YAE5D,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC5B,MAAM,IAAI,KAAK,CAAC,8BAAuB,OAAO,CAAE,CAAC,CAAC;YACpD,CAAC;YAEK,UAAU,GAAG,YAAE,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC9C,UAAU,GAAG,IAAA,6BAAgB,EAAC,UAAU,CAAC,CAAC;YAEhD,IAAI,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;gBAC5C,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;YAC/D,CAAC;YAEK,KAAK,GAAG,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC;YACjC,MAAM,GAAG,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC;YAErC,aAAa,GAAG,IAAA,eAAK,EAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAEzD,QAAQ,MAAM,EAAE,CAAC;gBACf,KAAK,KAAK,CAAC;gBACX,KAAK,MAAM;oBACT,aAAa,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,OAAO,SAAA,EAAE,CAAC,CAAC;oBAChD,MAAM;gBACR,KAAK,MAAM;oBACT,aAAa,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,OAAO,SAAA,EAAE,CAAC,CAAC;oBAChD,MAAM;gBACR,KAAK,KAAK,CAAC;gBACX;oBACE,aAAa,GAAG,aAAa,CAAC,GAAG,EAAE,CAAC;oBACpC,MAAM;YACV,CAAC;YAED,sBAAO,aAAa,CAAC,QAAQ,EAAE,EAAC;;;CACjC;AAED;;;;GAIG;AACH,SAAsB,OAAO,CAC3B,OAAe,EACf,OAAgB,EAChB,KAAc;;;;;;oBAEd,IAAI,CAAC,OAAO,EAAE,CAAC;wBACb,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;oBAC/C,CAAC;oBAEc,qBAAM,UAAU,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,aAAL,KAAK,cAAL,KAAK,GAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAA;;oBAAxE,MAAM,GAAG,SAA+D;oBAE9E,IAAI,OAAO,EAAE,CAAC;wBACZ,YAAE,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;wBAClC,sBAAO;oBACT,CAAC;oBAED,sBAAO,MAAM,EAAC;;;;CACf"}
1
+ {"version":3,"file":"image.js","sourceRoot":"","sources":["../../src/convert/image.ts"],"names":[],"mappings":";AAAA;;GAEG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAeH,gCAwCC;AAOD,0BAmBC;AA/ED,0CAAoB;AACpB,gDAA0B;AAC1B,iDAAsD;AAEtD,4DAA+E;AAE/E;;;;;;GAMG;AACH,SAAsB,UAAU;wDAC9B,OAAe,EACf,OAA+B;;QAA/B,wBAAA,EAAA,YAA+B;;YAEvB,KAA4C,OAAO,MAA1C,EAAT,KAAK,mBAAG,CAAC,KAAA,EAAE,KAAiC,OAAO,OAA1B,EAAd,MAAM,mBAAG,KAAK,KAAA,EAAE,KAAiB,OAAO,QAAZ,EAAZ,OAAO,mBAAG,EAAE,KAAA,CAAa;YAGtD,aAAa,GAAG,IAAA,kCAAgB,EAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;YAE1D,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;gBAClC,MAAM,IAAI,KAAK,CAAC,8BAAuB,aAAa,CAAE,CAAC,CAAC;YAC1D,CAAC;YAEK,UAAU,GAAG,YAAE,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;YACpD,UAAU,GAAG,IAAA,6BAAgB,EAAC,UAAU,CAAC,CAAC;YAEhD,IAAI,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;gBAC5C,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;YAC/D,CAAC;YAEK,KAAK,GAAG,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC;YACjC,MAAM,GAAG,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC;YAErC,aAAa,GAAG,IAAA,eAAK,EAAC,aAAa,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAE/D,QAAQ,MAAM,EAAE,CAAC;gBACf,KAAK,KAAK,CAAC;gBACX,KAAK,MAAM;oBACT,aAAa,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,OAAO,SAAA,EAAE,CAAC,CAAC;oBAChD,MAAM;gBACR,KAAK,MAAM;oBACT,aAAa,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,OAAO,SAAA,EAAE,CAAC,CAAC;oBAChD,MAAM;gBACR,KAAK,KAAK,CAAC;gBACX;oBACE,aAAa,GAAG,aAAa,CAAC,GAAG,EAAE,CAAC;oBACpC,MAAM;YACV,CAAC;YAED,sBAAO,aAAa,CAAC,QAAQ,EAAE,EAAC;;;CACjC;AAED;;;;GAIG;AACH,SAAsB,OAAO,CAC3B,OAAe,EACf,OAAgB,EAChB,KAAc;;;;;;oBAEd,IAAI,CAAC,OAAO,EAAE,CAAC;wBACb,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;oBAC/C,CAAC;oBAEc,qBAAM,UAAU,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,aAAL,KAAK,cAAL,KAAK,GAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAA;;oBAAxE,MAAM,GAAG,SAA+D;oBAE9E,IAAI,OAAO,EAAE,CAAC;wBAEN,kBAAkB,GAAG,IAAA,mCAAiB,EAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;wBAChE,YAAE,CAAC,aAAa,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;wBAC7C,sBAAO;oBACT,CAAC;oBAED,sBAAO,MAAM,EAAC;;;;CACf"}