helm-env-delta 1.15.0 → 1.15.2

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/README.md CHANGED
@@ -66,7 +66,7 @@ HelmEnvDelta (`hed`) automates environment synchronization for GitOps workflows
66
66
 
67
67
  ⚔ **High Performance** - Intelligent caching and parallel processing. Formatting rules, compiled regexes, and array normalization are all cached for fast repeated runs.
68
68
 
69
- šŸ” **Security Hardened** - Regex inputs (stop rules, transforms, pattern files) are validated against ReDoS (catastrophic backtracking). Fixed values are validated against prototype pollution attacks.
69
+ šŸ” **Security Hardened** - Regex inputs (stop rules, transforms, pattern files) are validated against ReDoS (catastrophic backtracking) — covers nested quantifiers, optional groups, and alternation patterns. Fixed values and YAML file content are guarded against prototype pollution. HTML report paths are HTML-escaped to prevent XSS from filename transforms.
70
70
 
71
71
  šŸ”” **Auto Updates** - Notifies when newer versions are available (skips in CI/CD).
72
72
 
@@ -428,7 +428,7 @@ requiredVersion: '1.10.0' # Optional: Minimum tool version required to process t
428
428
 
429
429
  **Note:** Source and destination paths cannot resolve to the same folder.
430
430
 
431
- **`requiredVersion`:** When set, the CLI checks that the installed version of helm-env-delta meets this minimum. If the installed version is older, the CLI exits immediately with a clear upgrade message. This prevents running configs that depend on newer features with an outdated tool version. Supports `"1.2.3"` or `"v1.2.3"` format.
431
+ **`requiredVersion`:** When set, the CLI checks that the installed version of helm-env-delta meets this minimum. If the installed version is older, the CLI exits immediately with a clear upgrade message. This prevents running configs that depend on newer features with an outdated tool version. Supports `"1.2.3"` or `"v1.2.3"` format. Setting `requiredVersion` also suppresses the auto-update notification — a pinned version requirement signals intentional version targeting.
432
432
 
433
433
  ---
434
434
 
@@ -680,7 +680,7 @@ stopRules:
680
680
 
681
681
  **Override:** Use `--force` to bypass stop rules when needed.
682
682
 
683
- **Regex safety:** All `regex` patterns (inline and from files) are validated against catastrophic backtracking (ReDoS). Patterns with nested quantifiers on groups (e.g., `(a+)+`) are rejected at config load time.
683
+ **Regex safety:** All `regex` patterns (inline and from files) are validated against catastrophic backtracking (ReDoS). Rejected patterns include: nested quantifiers on groups (e.g., `(a+)+`), optional groups with inner quantifiers (e.g., `(a+)?`), and alternation groups with outer repetition (e.g., `(a|ab)*`).
684
684
 
685
685
  **Visibility:** Stop rule violations appear in console output, JSON reports, and HTML reports (dry-run mode only, as a collapsible table in the header area).
686
686
 
package/dist/index.js CHANGED
@@ -78,6 +78,8 @@ const main = async () => {
78
78
  (0, node_fs_1.writeFileSync)(firstRunMarker, new Date().toISOString());
79
79
  }
80
80
  const config = (0, config_1.loadConfigFile)(command.config, command.quiet, logger, { formatOnly: command.formatOnly });
81
+ if (config.requiredVersion)
82
+ configHasRequiredVersion = true;
81
83
  if (command.showConfig) {
82
84
  console.log(chalk_1.default.cyan('\nāš™ļø Resolved Configuration:\n'));
83
85
  console.log(YAML.stringify(config, { indent: 2 }));
@@ -354,6 +356,7 @@ const main = async () => {
354
356
  if (command.diffJson)
355
357
  (0, reporters_1.generateJsonReport)(diffResult, formattedFiles, validationResult, syncConfig, command.dryRun, package_json_1.default.version);
356
358
  };
359
+ let configHasRequiredVersion = false;
357
360
  (async () => {
358
361
  try {
359
362
  await main();
@@ -393,7 +396,7 @@ const main = async () => {
393
396
  }
394
397
  finally {
395
398
  const command = (0, commandLine_1.parseCommandLine)();
396
- if (!command.quiet)
399
+ if (!command.quiet && !configHasRequiredVersion)
397
400
  void (0, versionChecker_1.checkForUpdates)(package_json_1.default.version);
398
401
  }
399
402
  })();
@@ -115,8 +115,9 @@ const deepMerge = (fullTarget, filteredSource, filteredTarget, currentPath = [],
115
115
  const fullTargetObject = fullTarget;
116
116
  const filteredTargetObject = filteredTarget || {};
117
117
  const result = { ...sourceObject };
118
+ const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
118
119
  for (const [key, value] of Object.entries(fullTargetObject))
119
- if (!(key in filteredTargetObject) && !(key in sourceObject))
120
+ if (!DANGEROUS_KEYS.has(key) && !(key in filteredTargetObject) && !(key in sourceObject))
120
121
  result[key] = value;
121
122
  for (const [key, value] of Object.entries(sourceObject))
122
123
  if (key in fullTargetObject)
@@ -51,13 +51,13 @@ const countDiffLines = (unifiedDiff) => {
51
51
  };
52
52
  const generateFileSummary = (file) => {
53
53
  if (!file.originalPath)
54
- return file.path;
55
- return `<span class="filename-transform">${file.originalPath} → ${file.path}</span>`;
54
+ return (0, treeRenderer_1.escapeHtml)(file.path);
55
+ return `<span class="filename-transform">${(0, treeRenderer_1.escapeHtml)(file.originalPath)} → ${(0, treeRenderer_1.escapeHtml)(file.path)}</span>`;
56
56
  };
57
57
  const generateAddedFileSummary = (file) => {
58
58
  if (!file.originalPath)
59
- return file.path;
60
- return `<span class="filename-transform">${file.originalPath} → ${file.path}</span>`;
59
+ return (0, treeRenderer_1.escapeHtml)(file.path);
60
+ return `<span class="filename-transform">${(0, treeRenderer_1.escapeHtml)(file.originalPath)} → ${(0, treeRenderer_1.escapeHtml)(file.path)}</span>`;
61
61
  };
62
62
  const JUMP_TO_SIDEBAR_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M2 2h3v12H2V2zm0-1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H2zm5 4h7v1H7V5zm0 3h7v1H7V8zm0 3h5v1H7v-1z"/></svg>`;
63
63
  const generateAddedFileSection = (file, fileId, open) => {
@@ -548,9 +548,9 @@ const calculateLevenshteinDistance = (string1, string2) => {
548
548
  matrix[0][col] = col;
549
549
  for (let row = 1; row <= string2.length; row++)
550
550
  for (let col = 1; col <= string1.length; col++)
551
- if (string2.charAt(row - 1) === string1.charAt(col - 1))
552
- matrix[row][col] = matrix[row - 1][col - 1];
553
- else
554
- matrix[row][col] = Math.min(matrix[row - 1][col - 1] + 1, matrix[row][col - 1] + 1, matrix[row - 1][col] + 1);
551
+ matrix[row][col] =
552
+ string2.charAt(row - 1) === string1.charAt(col - 1)
553
+ ? matrix[row - 1][col - 1]
554
+ : Math.min(matrix[row - 1][col - 1] + 1, matrix[row][col - 1] + 1, matrix[row - 1][col] + 1);
555
555
  return matrix[string2.length][string1.length];
556
556
  };
@@ -63,7 +63,7 @@ const getGitModifiedPaths = async (git, author, days, absoluteSourceDirectory) =
63
63
  }
64
64
  };
65
65
  const filterFileMapsByGitAuthor = async (sourceFiles, destinationFiles, absoluteSourceDirectory, author, days) => {
66
- const git = (0, simple_git_1.default)();
66
+ const git = (0, simple_git_1.default)(absoluteSourceDirectory);
67
67
  const gitRoot = await getGitRoot(git);
68
68
  const gitModifiedPaths = await getGitModifiedPaths(git, author, days, absoluteSourceDirectory);
69
69
  const matchingKeys = new Set();
@@ -2,7 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isSafeRegex = void 0;
4
4
  const isSafeRegex = (pattern) => {
5
- if (/\([^()]*[*+][^()]*\)[*+{]/.test(pattern))
5
+ if (/\([^()]*[*+][^()]*\)[*+?{]/.test(pattern))
6
+ return false;
7
+ if (/\([^()]*\|[^()]*\)[*+{]/.test(pattern))
6
8
  return false;
7
9
  return true;
8
10
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "helm-env-delta",
3
- "version": "1.15.0",
3
+ "version": "1.15.2",
4
4
  "description": "HelmEnvDelta – environment-aware YAML delta and sync for GitOps",
5
5
  "author": "BCsabaEngine",
6
6
  "license": "ISC",
@@ -67,27 +67,27 @@
67
67
  "@eslint/js": "^10.0.1",
68
68
  "@types/node": "^25.5.0",
69
69
  "@types/picomatch": "^4.0.2",
70
- "@typescript-eslint/eslint-plugin": "^8.57.1",
71
- "@vitest/coverage-v8": "^4.1.0",
72
- "eslint": "^10.0.3",
70
+ "@typescript-eslint/eslint-plugin": "^8.57.2",
71
+ "@vitest/coverage-v8": "^4.1.2",
72
+ "eslint": "^10.1.0",
73
73
  "eslint-config-prettier": "^10.1.8",
74
74
  "eslint-plugin-simple-import-sort": "^12.1.1",
75
- "eslint-plugin-unicorn": "^63.0.0",
75
+ "eslint-plugin-unicorn": "^64.0.0",
76
76
  "prettier": "^3.8.1",
77
77
  "tsx": "^4.21.0",
78
78
  "typescript": "^5.9.3",
79
- "vitest": "^4.1.0"
79
+ "vitest": "^4.1.2"
80
80
  },
81
81
  "dependencies": {
82
82
  "chalk": "^5.6.2",
83
83
  "commander": "^14.0.3",
84
- "diff": "^8.0.3",
84
+ "diff": "^8.0.4",
85
85
  "diff2html": "3.4.56",
86
86
  "open": "^11.0.0",
87
- "picomatch": "^4.0.3",
87
+ "picomatch": "^4.0.4",
88
88
  "simple-git": "^3.33.0",
89
89
  "tinyglobby": "^0.2.15",
90
- "yaml": "^2.8.2",
90
+ "yaml": "^2.8.3",
91
91
  "zod": "^4.3.6"
92
92
  }
93
93
  }