timonel 3.1.1 → 3.1.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.
Files changed (83) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +323 -695
  3. package/dist/cli.js +275 -15
  4. package/dist/index.d.ts +5 -1
  5. package/dist/index.js +12 -0
  6. package/dist/lib/helm.d.ts +471 -0
  7. package/dist/lib/helm.js +483 -0
  8. package/dist/lib/helmChartWriter.d.ts +157 -0
  9. package/dist/lib/helmChartWriter.js +171 -1
  10. package/dist/lib/policy/configurationLoader.d.ts +132 -0
  11. package/dist/lib/policy/configurationLoader.js +132 -0
  12. package/dist/lib/policy/errorContextGenerator.d.ts +89 -0
  13. package/dist/lib/policy/errorContextGenerator.js +99 -2
  14. package/dist/lib/policy/errors.d.ts +35 -0
  15. package/dist/lib/policy/errors.js +36 -0
  16. package/dist/lib/policy/index.d.ts +9 -0
  17. package/dist/lib/policy/index.js +16 -0
  18. package/dist/lib/policy/parallelExecutor.d.ts +90 -0
  19. package/dist/lib/policy/parallelExecutor.js +86 -3
  20. package/dist/lib/policy/pluginLoader.d.ts +92 -0
  21. package/dist/lib/policy/pluginLoader.js +92 -1
  22. package/dist/lib/policy/pluginRegistry.d.ts +54 -0
  23. package/dist/lib/policy/pluginRegistry.js +56 -0
  24. package/dist/lib/policy/policyEngine.d.ts +137 -0
  25. package/dist/lib/policy/policyEngine.js +191 -5
  26. package/dist/lib/policy/resultAggregator.d.ts +46 -0
  27. package/dist/lib/policy/resultAggregator.js +69 -1
  28. package/dist/lib/policy/resultFormatter.d.ts +88 -0
  29. package/dist/lib/policy/resultFormatter.js +101 -0
  30. package/dist/lib/policy/types.d.ts +136 -0
  31. package/dist/lib/policy/types.js +8 -0
  32. package/dist/lib/policy/validationCache.d.ts +146 -0
  33. package/dist/lib/policy/validationCache.js +142 -6
  34. package/dist/lib/resources/baseResourceProvider.d.ts +45 -0
  35. package/dist/lib/resources/baseResourceProvider.js +48 -1
  36. package/dist/lib/resources/cloud/aws/awsResources.d.ts +192 -0
  37. package/dist/lib/resources/cloud/aws/awsResources.js +163 -1
  38. package/dist/lib/resources/cloud/aws/karpenterResources.d.ts +131 -0
  39. package/dist/lib/resources/cloud/aws/karpenterResources.js +77 -0
  40. package/dist/lib/rutter.d.ts +381 -3
  41. package/dist/lib/rutter.js +439 -28
  42. package/dist/lib/security.d.ts +123 -0
  43. package/dist/lib/security.js +162 -4
  44. package/dist/lib/templates/flexible-subchart.d.ts +52 -0
  45. package/dist/lib/templates/flexible-subchart.js +70 -0
  46. package/dist/lib/templates/umbrella-chart.d.ts +27 -0
  47. package/dist/lib/templates/umbrella-chart.js +89 -0
  48. package/dist/lib/types.d.ts +26 -0
  49. package/dist/lib/umbrella.d.ts +23 -0
  50. package/dist/lib/umbrella.js +23 -0
  51. package/dist/lib/umbrellaRutter.d.ts +75 -0
  52. package/dist/lib/umbrellaRutter.js +82 -2
  53. package/dist/lib/utils/envVarsLoader.d.ts +49 -0
  54. package/dist/lib/utils/envVarsLoader.js +53 -0
  55. package/dist/lib/utils/helmConstructSerializer.d.ts +17 -0
  56. package/dist/lib/utils/helmConstructSerializer.js +22 -0
  57. package/dist/lib/utils/helmControlStructures.d.ts +194 -0
  58. package/dist/lib/utils/helmControlStructures.js +180 -0
  59. package/dist/lib/utils/helmHelpers/envHelpers.d.ts +13 -0
  60. package/dist/lib/utils/helmHelpers/envHelpers.js +13 -0
  61. package/dist/lib/utils/helmHelpers/gitopsHelpers.d.ts +13 -0
  62. package/dist/lib/utils/helmHelpers/gitopsHelpers.js +13 -0
  63. package/dist/lib/utils/helmHelpers/index.d.ts +74 -0
  64. package/dist/lib/utils/helmHelpers/index.js +85 -1
  65. package/dist/lib/utils/helmHelpers/observabilityHelpers.d.ts +13 -0
  66. package/dist/lib/utils/helmHelpers/observabilityHelpers.js +13 -0
  67. package/dist/lib/utils/helmHelpers/types.d.ts +23 -0
  68. package/dist/lib/utils/helmHelpers/types.js +4 -0
  69. package/dist/lib/utils/helmHelpers/validationHelpers.d.ts +13 -0
  70. package/dist/lib/utils/helmHelpers/validationHelpers.js +13 -0
  71. package/dist/lib/utils/helmHelpers.d.ts +62 -0
  72. package/dist/lib/utils/helmHelpers.js +77 -0
  73. package/dist/lib/utils/helmYamlSerializer.d.ts +77 -0
  74. package/dist/lib/utils/helmYamlSerializer.js +398 -21
  75. package/dist/lib/utils/logger.d.ts +153 -0
  76. package/dist/lib/utils/logger.js +170 -2
  77. package/dist/lib/utils/valuesRef.d.ts +181 -50
  78. package/dist/lib/utils/valuesRef.js +168 -170
  79. package/dist/lib/validation/inputValidator.d.ts +45 -0
  80. package/dist/lib/validation/inputValidator.js +67 -2
  81. package/dist/types/index.d.ts +34 -0
  82. package/dist/types/index.js +3 -0
  83. package/package.json +31 -38
@@ -1,5 +1,17 @@
1
+ /**
2
+ * Policy Engine Implementation
3
+ *
4
+ * This module provides the main PolicyEngine class that orchestrates policy validation
5
+ * through registered plugins. It implements the core policy engine interface with
6
+ * plugin management, validation orchestration, and result aggregation.
7
+ *
8
+ * @since 3.0.0
9
+ */
1
10
  import type { ChartMetadata } from '../rutter.js';
2
11
  import type { PolicyEngine as IPolicyEngine, PolicyPlugin, PolicyEngineOptions, PolicyResult, PolicyViolation, PolicyWarning, ValidationContext } from './types.js';
12
+ /**
13
+ * Main policy engine implementation
14
+ */
3
15
  export declare class PolicyEngine implements IPolicyEngine {
4
16
  private readonly registry;
5
17
  private options;
@@ -10,30 +22,155 @@ export declare class PolicyEngine implements IPolicyEngine {
10
22
  private _cache?;
11
23
  private _parallelExecutor?;
12
24
  constructor(options?: PolicyEngineOptions);
25
+ /**
26
+ * Lazy getter for configuration loader
27
+ * @private
28
+ */
13
29
  private get configurationLoader();
30
+ /**
31
+ * Lazy getter for validation cache
32
+ * @private
33
+ */
14
34
  private get cache();
35
+ /**
36
+ * Lazy getter for parallel executor
37
+ * @private
38
+ */
15
39
  private get parallelExecutor();
40
+ /**
41
+ * Registers a policy plugin for validation
42
+ * @param plugin - The policy plugin to register
43
+ * @returns The engine instance for method chaining
44
+ */
16
45
  use(plugin: PolicyPlugin): Promise<PolicyEngine>;
46
+ /**
47
+ * Validates Kubernetes manifests against registered policies
48
+ * @param manifests - Array of Kubernetes manifest objects
49
+ * @param chartMetadata - Chart metadata for validation context
50
+ * @returns Promise resolving to validation results
51
+ */
17
52
  validate(manifests: unknown[], chartMetadata: ChartMetadata): Promise<PolicyResult>;
53
+ /**
54
+ * Configures engine-wide settings
55
+ * @param options - Configuration options
56
+ */
18
57
  configure(options: PolicyEngineOptions): PolicyEngine;
58
+ /**
59
+ * Formats validation results using the configured formatter
60
+ * @param result - Validation result to format
61
+ * @returns Formatted string representation
62
+ */
19
63
  formatResult(result: PolicyResult): string;
64
+ /**
65
+ * Generates detailed error context for a violation
66
+ * @param violation - Policy violation
67
+ * @param validationContext - Validation context
68
+ * @param allViolations - All violations for finding related issues
69
+ * @param executionTime - Plugin execution time
70
+ * @returns Detailed error context
71
+ */
20
72
  generateErrorContext(violation: PolicyViolation | PolicyWarning, validationContext?: ValidationContext, allViolations?: (PolicyViolation | PolicyWarning)[], executionTime?: number): import("./errorContextGenerator.js").ErrorContext;
73
+ /**
74
+ * Generates a detailed error report for a violation
75
+ * @param violation - Policy violation
76
+ * @param validationContext - Validation context
77
+ * @param allViolations - All violations for finding related issues
78
+ * @param executionTime - Plugin execution time
79
+ * @returns Formatted error report string
80
+ */
21
81
  generateErrorReport(violation: PolicyViolation | PolicyWarning, validationContext?: ValidationContext, allViolations?: (PolicyViolation | PolicyWarning)[], executionTime?: number): string;
82
+ /**
83
+ * Gets cache statistics for monitoring
84
+ * @returns Cache statistics
85
+ */
22
86
  getCacheStats(): import("./validationCache.js").CacheStats;
87
+ /**
88
+ * Invalidates cache entries based on criteria
89
+ * @param criteria - Invalidation criteria
90
+ * @returns Number of invalidated entries
91
+ */
23
92
  invalidateCache(criteria: {
24
93
  manifestHash?: string;
25
94
  pluginHash?: string;
26
95
  olderThan?: number;
27
96
  all?: boolean;
28
97
  }): number;
98
+ /**
99
+ * Clears all cache statistics
100
+ */
29
101
  clearCacheStats(): void;
102
+ /**
103
+ * Executes plugins sequentially
104
+ * @param plugins - Array of plugins to execute
105
+ * @param manifests - Manifests to validate
106
+ * @param violations - Array to collect violations
107
+ * @param warnings - Array to collect warnings
108
+ * @private
109
+ */
30
110
  private executeSequential;
111
+ /**
112
+ * Executes plugins in parallel using the optimized parallel executor
113
+ * @param plugins - Array of plugins to execute
114
+ * @param manifests - Manifests to validate
115
+ * @param violations - Array to collect violations
116
+ * @param warnings - Array to collect warnings
117
+ * @private
118
+ */
31
119
  private executeParallelOptimized;
120
+ /**
121
+ * Executes a single plugin with timeout and retry handling
122
+ * @param plugin - Plugin to execute
123
+ * @param manifests - Manifests to validate
124
+ * @returns Promise resolving to plugin violations
125
+ * @private
126
+ */
32
127
  private executePlugin;
128
+ /**
129
+ * Executes a single plugin attempt with timeout handling
130
+ * @param plugin - Plugin to execute
131
+ * @param manifests - Manifests to validate
132
+ * @param attempt - Current attempt number
133
+ * @returns Promise resolving to plugin violations
134
+ * @private
135
+ */
33
136
  private executePluginAttempt;
137
+ /**
138
+ * Determines if an error should trigger a retry
139
+ * @param error - Error that occurred
140
+ * @param retryConfig - Retry configuration
141
+ * @returns True if the error should be retried
142
+ * @private
143
+ */
34
144
  private shouldRetryError;
145
+ /**
146
+ * Sleep for the specified number of milliseconds
147
+ * @param ms - Milliseconds to sleep
148
+ * @returns Promise that resolves after the delay
149
+ * @private
150
+ */
35
151
  private sleep;
152
+ /**
153
+ * Creates a timeout promise that rejects after the specified time
154
+ * @param timeout - Timeout in milliseconds
155
+ * @param pluginName - Name of the plugin for error reporting
156
+ * @returns Promise that rejects with timeout error
157
+ * @private
158
+ */
36
159
  private createTimeoutPromise;
160
+ /**
161
+ * Handles plugin execution errors with enhanced isolation and context
162
+ * @param plugin - Plugin that failed
163
+ * @param error - Error that occurred
164
+ * @param violations - Array to add error violation to
165
+ * @private
166
+ */
37
167
  private handlePluginError;
168
+ /**
169
+ * Generates helpful suggestions based on the error type
170
+ * @param error - Error that occurred
171
+ * @param plugin - Plugin that failed
172
+ * @returns Suggestion string
173
+ * @private
174
+ */
38
175
  private generateErrorSuggestion;
39
176
  }
@@ -1,3 +1,12 @@
1
+ /**
2
+ * Policy Engine Implementation
3
+ *
4
+ * This module provides the main PolicyEngine class that orchestrates policy validation
5
+ * through registered plugins. It implements the core policy engine interface with
6
+ * plugin management, validation orchestration, and result aggregation.
7
+ *
8
+ * @since 3.0.0
9
+ */
1
10
  import { createLogger } from '../utils/logger.js';
2
11
  import { InputValidator } from '../validation/inputValidator.js';
3
12
  import { PluginRegistry } from './pluginRegistry.js';
@@ -8,13 +17,22 @@ import { ErrorContextGenerator } from './errorContextGenerator.js';
8
17
  import { ConfigurationLoader } from './configurationLoader.js';
9
18
  import { ValidationCache, generateManifestHash, generatePluginHash } from './validationCache.js';
10
19
  import { ParallelExecutor, calculateOptimalConcurrency, } from './parallelExecutor.js';
20
+ /**
21
+ * Constants for error messages
22
+ */
11
23
  const UNKNOWN_ERROR_MESSAGE = 'Unknown error';
24
+ /**
25
+ * Default policy engine configuration
26
+ */
12
27
  const DEFAULT_OPTIONS = {
13
28
  timeout: 5000,
14
29
  parallel: false,
15
30
  failFast: false,
16
31
  gracefulDegradation: true,
17
32
  };
33
+ /**
34
+ * Default retry configuration
35
+ */
18
36
  const DEFAULT_RETRY_CONFIG = {
19
37
  maxAttempts: 3,
20
38
  baseDelay: 1000,
@@ -23,6 +41,9 @@ const DEFAULT_RETRY_CONFIG = {
23
41
  retryOnTimeout: true,
24
42
  retryOnPluginError: false,
25
43
  };
44
+ /**
45
+ * Common operation names for logging
46
+ */
26
47
  const OPERATIONS = {
27
48
  PLUGIN_REGISTRATION: 'plugin_registration',
28
49
  VALIDATION_START: 'validation_start',
@@ -30,6 +51,9 @@ const OPERATIONS = {
30
51
  PLUGIN_EXECUTION_ERROR: 'plugin_execution_error_isolated',
31
52
  PLUGIN_EXECUTION_RETRY: 'plugin_execution_retry',
32
53
  };
54
+ /**
55
+ * Main policy engine implementation
56
+ */
33
57
  export class PolicyEngine {
34
58
  constructor(options = {}) {
35
59
  this.registry = new PluginRegistry();
@@ -44,6 +68,10 @@ export class PolicyEngine {
44
68
  operation: 'policy_engine_init',
45
69
  });
46
70
  }
71
+ /**
72
+ * Lazy getter for configuration loader
73
+ * @private
74
+ */
47
75
  get configurationLoader() {
48
76
  if (!this._configurationLoader) {
49
77
  this._configurationLoader = new ConfigurationLoader(this.options.configurationLoader);
@@ -53,6 +81,10 @@ export class PolicyEngine {
53
81
  }
54
82
  return this._configurationLoader;
55
83
  }
84
+ /**
85
+ * Lazy getter for validation cache
86
+ * @private
87
+ */
56
88
  get cache() {
57
89
  if (!this._cache) {
58
90
  this._cache = new ValidationCache(this.options.cacheOptions);
@@ -63,10 +95,14 @@ export class PolicyEngine {
63
95
  }
64
96
  return this._cache;
65
97
  }
98
+ /**
99
+ * Lazy getter for parallel executor
100
+ * @private
101
+ */
66
102
  get parallelExecutor() {
67
103
  if (!this._parallelExecutor) {
68
104
  const parallelOptions = {
69
- maxConcurrency: this.options.parallelOptions?.maxConcurrency || calculateOptimalConcurrency(10),
105
+ maxConcurrency: this.options.parallelOptions?.maxConcurrency || calculateOptimalConcurrency(10), // Default estimate
70
106
  pluginTimeout: this.options.timeout || DEFAULT_OPTIONS.timeout,
71
107
  failFast: this.options.failFast || DEFAULT_OPTIONS.failFast,
72
108
  ...this.options.parallelOptions,
@@ -79,8 +115,14 @@ export class PolicyEngine {
79
115
  }
80
116
  return this._parallelExecutor;
81
117
  }
118
+ /**
119
+ * Registers a policy plugin for validation
120
+ * @param plugin - The policy plugin to register
121
+ * @returns The engine instance for method chaining
122
+ */
82
123
  async use(plugin) {
83
124
  try {
125
+ // Input validation for plugin
84
126
  if (!plugin || typeof plugin !== 'object') {
85
127
  throw new PolicyValidationError('Plugin must be a non-null object');
86
128
  }
@@ -93,11 +135,13 @@ export class PolicyEngine {
93
135
  if (typeof plugin.validate !== 'function') {
94
136
  throw new PolicyValidationError('Plugin must have a validate method');
95
137
  }
138
+ // Validate plugin name length and content
96
139
  if (plugin.name.length > 100) {
97
140
  throw new PolicyValidationError('Plugin name exceeds maximum length (100 characters)');
98
141
  }
142
+ // Sanitize plugin name to prevent injection
99
143
  const sanitizedName = plugin.name
100
- .replace(/[<>"'&]/g, '')
144
+ .replace(/[<>"'&]/g, '') // Remove potentially dangerous characters
101
145
  .trim();
102
146
  if (sanitizedName !== plugin.name) {
103
147
  this.logger.warn('Plugin name was sanitized', {
@@ -106,7 +150,9 @@ export class PolicyEngine {
106
150
  operation: 'plugin_name_sanitization',
107
151
  });
108
152
  }
153
+ // Load configuration using the configuration loader
109
154
  const pluginConfiguration = await this.configurationLoader.loadPluginConfiguration(plugin, this.options.environment, this.options.pluginConfig?.[plugin.name]);
155
+ // Register plugin with loaded configuration
110
156
  this.registry.register(plugin, pluginConfiguration.config);
111
157
  this.logger.info('Plugin registered successfully', {
112
158
  pluginName: plugin.name,
@@ -116,6 +162,7 @@ export class PolicyEngine {
116
162
  configSources: pluginConfiguration.entries.map((e) => e.source),
117
163
  operation: OPERATIONS.PLUGIN_REGISTRATION,
118
164
  });
165
+ // Log configuration validation warnings if any
119
166
  if (pluginConfiguration.validationErrors) {
120
167
  this.logger.warn('Plugin configuration validation warnings', {
121
168
  pluginName: plugin.name,
@@ -134,23 +181,36 @@ export class PolicyEngine {
134
181
  throw error;
135
182
  }
136
183
  }
184
+ /**
185
+ * Validates Kubernetes manifests against registered policies
186
+ * @param manifests - Array of Kubernetes manifest objects
187
+ * @param chartMetadata - Chart metadata for validation context
188
+ * @returns Promise resolving to validation results
189
+ */
137
190
  async validate(manifests, chartMetadata) {
138
191
  const startTime = Date.now();
192
+ // Input validation - validate manifests array
139
193
  if (!Array.isArray(manifests)) {
140
194
  throw new PolicyValidationError('Manifests must be an array');
141
195
  }
196
+ // Validate array length
142
197
  if (manifests.length > 1000) {
198
+ // Reasonable limit for manifests
143
199
  throw new PolicyValidationError('Too many manifests provided (maximum: 1000)');
144
200
  }
201
+ // Validate each manifest
145
202
  manifests.forEach((manifest, index) => {
146
203
  if (manifest === null || manifest === undefined) {
147
204
  throw new PolicyValidationError(`Manifest at index ${index} cannot be null or undefined`);
148
205
  }
206
+ // Validate manifest size to prevent memory exhaustion
149
207
  const manifestStr = JSON.stringify(manifest);
150
208
  if (manifestStr.length > 100000) {
209
+ // 100KB limit per manifest
151
210
  throw new PolicyValidationError(`Manifest at index ${index} exceeds size limit (100KB)`);
152
211
  }
153
212
  });
213
+ // Validate chartMetadata
154
214
  if (!chartMetadata || typeof chartMetadata !== 'object') {
155
215
  throw new PolicyValidationError('Chart metadata must be a non-null object');
156
216
  }
@@ -160,6 +220,7 @@ export class PolicyEngine {
160
220
  pluginCount: plugins.length,
161
221
  operation: OPERATIONS.VALIDATION_START,
162
222
  });
223
+ // Return successful result if no plugins are registered
163
224
  if (plugins.length === 0) {
164
225
  const metadata = {
165
226
  executionTime: Date.now() - startTime,
@@ -183,10 +244,12 @@ export class PolicyEngine {
183
244
  },
184
245
  };
185
246
  }
247
+ // Generate cache keys
186
248
  const manifestHash = generateManifestHash(manifests);
187
249
  const pluginNames = plugins.map((p) => p.name);
188
250
  const pluginConfigs = Object.fromEntries(pluginNames.map((name) => [name, this.registry.getPluginConfig(name)]));
189
251
  const pluginHash = generatePluginHash(pluginNames, pluginConfigs);
252
+ // Check cache first
190
253
  const cachedResult = this.cache.get(manifestHash, pluginHash);
191
254
  if (cachedResult) {
192
255
  this.logger.info('Returning cached validation result', {
@@ -230,6 +293,7 @@ export class PolicyEngine {
230
293
  metadata,
231
294
  summary: generateResultSummary(violations, warnings, plugins.map((p) => p.name)),
232
295
  };
296
+ // Cache the result
233
297
  this.cache.set(manifestHash, pluginHash, result);
234
298
  this.logger.info('Policy validation completed', {
235
299
  valid: result.valid,
@@ -241,6 +305,10 @@ export class PolicyEngine {
241
305
  });
242
306
  return result;
243
307
  }
308
+ /**
309
+ * Configures engine-wide settings
310
+ * @param options - Configuration options
311
+ */
244
312
  configure(options) {
245
313
  this.options = { ...this.options, ...options };
246
314
  this.logger.debug('PolicyEngine configuration updated', {
@@ -249,29 +317,72 @@ export class PolicyEngine {
249
317
  });
250
318
  return this;
251
319
  }
320
+ /**
321
+ * Formats validation results using the configured formatter
322
+ * @param result - Validation result to format
323
+ * @returns Formatted string representation
324
+ */
252
325
  formatResult(result) {
253
326
  const formatter = this.options.formatter || new DefaultResultFormatter();
254
327
  return formatter.format(result);
255
328
  }
329
+ /**
330
+ * Generates detailed error context for a violation
331
+ * @param violation - Policy violation
332
+ * @param validationContext - Validation context
333
+ * @param allViolations - All violations for finding related issues
334
+ * @param executionTime - Plugin execution time
335
+ * @returns Detailed error context
336
+ */
256
337
  generateErrorContext(violation, validationContext, allViolations, executionTime) {
257
338
  return this.errorContextGenerator.generateContext(violation, validationContext, allViolations, executionTime);
258
339
  }
340
+ /**
341
+ * Generates a detailed error report for a violation
342
+ * @param violation - Policy violation
343
+ * @param validationContext - Validation context
344
+ * @param allViolations - All violations for finding related issues
345
+ * @param executionTime - Plugin execution time
346
+ * @returns Formatted error report string
347
+ */
259
348
  generateErrorReport(violation, validationContext, allViolations, executionTime) {
260
349
  return this.errorContextGenerator.generateErrorReport(violation, validationContext, allViolations, executionTime);
261
350
  }
351
+ /**
352
+ * Gets cache statistics for monitoring
353
+ * @returns Cache statistics
354
+ */
262
355
  getCacheStats() {
263
356
  return this.cache.getStats();
264
357
  }
358
+ /**
359
+ * Invalidates cache entries based on criteria
360
+ * @param criteria - Invalidation criteria
361
+ * @returns Number of invalidated entries
362
+ */
265
363
  invalidateCache(criteria) {
266
364
  return this.cache.invalidate(criteria);
267
365
  }
366
+ /**
367
+ * Clears all cache statistics
368
+ */
268
369
  clearCacheStats() {
269
370
  this.cache.clearStats();
270
371
  }
372
+ /**
373
+ * Executes plugins sequentially
374
+ * @param plugins - Array of plugins to execute
375
+ * @param manifests - Manifests to validate
376
+ * @param violations - Array to collect violations
377
+ * @param warnings - Array to collect warnings
378
+ * @private
379
+ */
380
+ // eslint-disable-next-line sonarjs/cognitive-complexity
271
381
  async executeSequential(plugins, manifests, violations, warnings, chartMetadata) {
272
382
  for (const plugin of plugins) {
273
383
  try {
274
384
  const pluginViolations = await this.executePlugin(plugin, manifests, chartMetadata);
385
+ // Separate violations by severity
275
386
  for (const violation of pluginViolations) {
276
387
  if (violation.severity === 'error') {
277
388
  violations.push(violation);
@@ -280,6 +391,7 @@ export class PolicyEngine {
280
391
  warnings.push(violation);
281
392
  }
282
393
  }
394
+ // Fail fast if enabled and we have errors
283
395
  if (this.options.failFast && violations.length > 0) {
284
396
  this.logger.debug('Fail fast enabled, stopping validation', {
285
397
  pluginName: plugin.name,
@@ -290,18 +402,30 @@ export class PolicyEngine {
290
402
  }
291
403
  }
292
404
  catch (error) {
405
+ // Handle plugin execution errors
293
406
  if (this.options.gracefulDegradation) {
407
+ // With graceful degradation, convert error to violation
294
408
  this.handlePluginError(plugin, error, violations);
295
409
  }
296
410
  else {
411
+ // Without graceful degradation, re-throw the error
297
412
  throw error;
298
413
  }
299
414
  }
300
415
  }
301
416
  }
417
+ /**
418
+ * Executes plugins in parallel using the optimized parallel executor
419
+ * @param plugins - Array of plugins to execute
420
+ * @param manifests - Manifests to validate
421
+ * @param violations - Array to collect violations
422
+ * @param warnings - Array to collect warnings
423
+ * @private
424
+ */
425
+ // eslint-disable-next-line sonarjs/cognitive-complexity
302
426
  async executeParallelOptimized(plugins, manifests, violations, warnings, chartMetadata) {
303
427
  const validationContext = {
304
- chart: chartMetadata,
428
+ chart: chartMetadata, // Use actual chart metadata instead of hardcoded values
305
429
  environment: this.options.environment || 'development',
306
430
  logger: this.logger,
307
431
  };
@@ -315,16 +439,20 @@ export class PolicyEngine {
315
439
  totalMemoryMB: stats.resourceUsage.totalMemoryMB,
316
440
  operation: 'parallel_execution_stats',
317
441
  });
442
+ // Process results
318
443
  for (const result of results) {
319
444
  if (result.error) {
320
445
  if (this.options.gracefulDegradation) {
446
+ // With graceful degradation, convert error to violation
321
447
  this.handlePluginError(result.plugin, result.error, violations);
322
448
  }
323
449
  else {
450
+ // Without graceful degradation, throw the error
324
451
  throw result.error;
325
452
  }
326
453
  }
327
454
  else {
455
+ // Separate violations by severity
328
456
  for (const violation of result.violations) {
329
457
  if (violation.severity === 'error') {
330
458
  violations.push(violation);
@@ -334,6 +462,7 @@ export class PolicyEngine {
334
462
  }
335
463
  }
336
464
  }
465
+ // Fail fast if enabled and we have errors
337
466
  if (this.options.failFast && violations.length > 0) {
338
467
  this.logger.debug('Fail fast enabled, stopping validation', {
339
468
  pluginName: result.plugin.name,
@@ -344,6 +473,13 @@ export class PolicyEngine {
344
473
  }
345
474
  }
346
475
  }
476
+ /**
477
+ * Executes a single plugin with timeout and retry handling
478
+ * @param plugin - Plugin to execute
479
+ * @param manifests - Manifests to validate
480
+ * @returns Promise resolving to plugin violations
481
+ * @private
482
+ */
347
483
  async executePlugin(plugin, manifests, chartMetadata) {
348
484
  const retryConfig = { ...DEFAULT_RETRY_CONFIG, ...this.options.retryConfig };
349
485
  let lastError = null;
@@ -360,10 +496,12 @@ export class PolicyEngine {
360
496
  error: lastError.message,
361
497
  operation: 'plugin_execution_retry',
362
498
  });
499
+ // Check if we should retry this error type
363
500
  const shouldRetry = this.shouldRetryError(lastError, retryConfig);
364
501
  if (!shouldRetry || attempt === retryConfig.maxAttempts) {
365
502
  break;
366
503
  }
504
+ // Calculate delay with exponential backoff
367
505
  const delay = Math.min(retryConfig.baseDelay * Math.pow(retryConfig.backoffMultiplier, attempt - 1), retryConfig.maxDelay);
368
506
  this.logger.debug('Retrying plugin execution after delay', {
369
507
  pluginName: plugin.name,
@@ -374,10 +512,20 @@ export class PolicyEngine {
374
512
  await this.sleep(delay);
375
513
  }
376
514
  }
515
+ // All retry attempts exhausted - always throw error
516
+ // Let the calling method decide how to handle it based on gracefulDegradation setting
377
517
  const finalError = lastError ||
378
518
  new Error(`Plugin '${plugin.name}' failed with unknown error after ${retryConfig.maxAttempts} attempts`);
379
519
  throw new PluginRetryExhaustedError(`Plugin '${plugin.name}' failed after ${retryConfig.maxAttempts} attempts`, plugin.name, retryConfig.maxAttempts, finalError);
380
520
  }
521
+ /**
522
+ * Executes a single plugin attempt with timeout handling
523
+ * @param plugin - Plugin to execute
524
+ * @param manifests - Manifests to validate
525
+ * @param attempt - Current attempt number
526
+ * @returns Promise resolving to plugin violations
527
+ * @private
528
+ */
381
529
  async executePluginAttempt(plugin, manifests, attempt, chartMetadata) {
382
530
  const timeout = this.options.timeout || DEFAULT_OPTIONS.timeout;
383
531
  this.logger.debug('Executing plugin attempt', {
@@ -387,7 +535,7 @@ export class PolicyEngine {
387
535
  operation: 'plugin_execution_attempt_start',
388
536
  });
389
537
  const validationContext = {
390
- chart: chartMetadata,
538
+ chart: chartMetadata, // Use actual chart metadata instead of hardcoded values
391
539
  config: this.registry.getPluginConfig(plugin.name),
392
540
  environment: this.options.environment || 'development',
393
541
  logger: this.logger,
@@ -424,6 +572,13 @@ export class PolicyEngine {
424
572
  throw error;
425
573
  }
426
574
  }
575
+ /**
576
+ * Determines if an error should trigger a retry
577
+ * @param error - Error that occurred
578
+ * @param retryConfig - Retry configuration
579
+ * @returns True if the error should be retried
580
+ * @private
581
+ */
427
582
  shouldRetryError(error, retryConfig) {
428
583
  if (error instanceof ValidationTimeoutError) {
429
584
  return retryConfig.retryOnTimeout;
@@ -431,11 +586,25 @@ export class PolicyEngine {
431
586
  if (error instanceof PluginError) {
432
587
  return retryConfig.retryOnPluginError;
433
588
  }
589
+ // Retry on generic errors if plugin error retry is enabled
434
590
  return retryConfig.retryOnPluginError;
435
591
  }
592
+ /**
593
+ * Sleep for the specified number of milliseconds
594
+ * @param ms - Milliseconds to sleep
595
+ * @returns Promise that resolves after the delay
596
+ * @private
597
+ */
436
598
  sleep(ms) {
437
599
  return new Promise((resolve) => globalThis.setTimeout(resolve, ms));
438
600
  }
601
+ /**
602
+ * Creates a timeout promise that rejects after the specified time
603
+ * @param timeout - Timeout in milliseconds
604
+ * @param pluginName - Name of the plugin for error reporting
605
+ * @returns Promise that rejects with timeout error
606
+ * @private
607
+ */
439
608
  createTimeoutPromise(timeout, pluginName) {
440
609
  return new Promise((_, reject) => {
441
610
  globalThis.setTimeout(() => {
@@ -443,6 +612,13 @@ export class PolicyEngine {
443
612
  }, timeout);
444
613
  });
445
614
  }
615
+ /**
616
+ * Handles plugin execution errors with enhanced isolation and context
617
+ * @param plugin - Plugin that failed
618
+ * @param error - Error that occurred
619
+ * @param violations - Array to add error violation to
620
+ * @private
621
+ */
446
622
  handlePluginError(plugin, error, violations) {
447
623
  const errorMessage = error instanceof Error ? error.message : UNKNOWN_ERROR_MESSAGE;
448
624
  const errorStack = error instanceof Error ? error.stack : String(error);
@@ -453,14 +629,16 @@ export class PolicyEngine {
453
629
  errorType: error instanceof Error ? error.constructor.name : typeof error,
454
630
  operation: 'plugin_execution_error_isolated',
455
631
  });
632
+ // Create detailed error context for debugging
456
633
  const errorContext = {
457
634
  error: errorStack,
458
635
  pluginVersion: plugin.version,
459
636
  pluginMetadata: plugin.metadata,
460
637
  timestamp: new Date().toISOString(),
461
638
  errorType: error instanceof Error ? error.constructor.name : typeof error,
462
- isolated: true,
639
+ isolated: true, // Mark as isolated error
463
640
  };
641
+ // Add plugin error as a violation with enhanced context
464
642
  const errorViolation = {
465
643
  plugin: plugin.name,
466
644
  severity: 'error',
@@ -471,12 +649,20 @@ export class PolicyEngine {
471
649
  context: errorContext,
472
650
  };
473
651
  violations.push(errorViolation);
652
+ // Log additional context for debugging
474
653
  this.logger.debug('Plugin error context generated', {
475
654
  pluginName: plugin.name,
476
655
  errorContext,
477
656
  operation: 'plugin_error_context',
478
657
  });
479
658
  }
659
+ /**
660
+ * Generates helpful suggestions based on the error type
661
+ * @param error - Error that occurred
662
+ * @param plugin - Plugin that failed
663
+ * @returns Suggestion string
664
+ * @private
665
+ */
480
666
  generateErrorSuggestion(error, plugin) {
481
667
  if (error instanceof ValidationTimeoutError) {
482
668
  return `Consider increasing the timeout value or optimizing the plugin '${plugin.name}' for better performance.`;