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.
- package/CHANGELOG.md +7 -0
- package/README.md +323 -695
- package/dist/cli.js +275 -15
- package/dist/index.d.ts +5 -1
- package/dist/index.js +12 -0
- package/dist/lib/helm.d.ts +471 -0
- package/dist/lib/helm.js +483 -0
- package/dist/lib/helmChartWriter.d.ts +157 -0
- package/dist/lib/helmChartWriter.js +171 -1
- package/dist/lib/policy/configurationLoader.d.ts +132 -0
- package/dist/lib/policy/configurationLoader.js +132 -0
- package/dist/lib/policy/errorContextGenerator.d.ts +89 -0
- package/dist/lib/policy/errorContextGenerator.js +99 -2
- package/dist/lib/policy/errors.d.ts +35 -0
- package/dist/lib/policy/errors.js +36 -0
- package/dist/lib/policy/index.d.ts +9 -0
- package/dist/lib/policy/index.js +16 -0
- package/dist/lib/policy/parallelExecutor.d.ts +90 -0
- package/dist/lib/policy/parallelExecutor.js +86 -3
- package/dist/lib/policy/pluginLoader.d.ts +92 -0
- package/dist/lib/policy/pluginLoader.js +92 -1
- package/dist/lib/policy/pluginRegistry.d.ts +54 -0
- package/dist/lib/policy/pluginRegistry.js +56 -0
- package/dist/lib/policy/policyEngine.d.ts +137 -0
- package/dist/lib/policy/policyEngine.js +191 -5
- package/dist/lib/policy/resultAggregator.d.ts +46 -0
- package/dist/lib/policy/resultAggregator.js +69 -1
- package/dist/lib/policy/resultFormatter.d.ts +88 -0
- package/dist/lib/policy/resultFormatter.js +101 -0
- package/dist/lib/policy/types.d.ts +136 -0
- package/dist/lib/policy/types.js +8 -0
- package/dist/lib/policy/validationCache.d.ts +146 -0
- package/dist/lib/policy/validationCache.js +142 -6
- package/dist/lib/resources/baseResourceProvider.d.ts +45 -0
- package/dist/lib/resources/baseResourceProvider.js +48 -1
- package/dist/lib/resources/cloud/aws/awsResources.d.ts +192 -0
- package/dist/lib/resources/cloud/aws/awsResources.js +163 -1
- package/dist/lib/resources/cloud/aws/karpenterResources.d.ts +131 -0
- package/dist/lib/resources/cloud/aws/karpenterResources.js +77 -0
- package/dist/lib/rutter.d.ts +381 -3
- package/dist/lib/rutter.js +439 -28
- package/dist/lib/security.d.ts +123 -0
- package/dist/lib/security.js +162 -4
- package/dist/lib/templates/flexible-subchart.d.ts +52 -0
- package/dist/lib/templates/flexible-subchart.js +70 -0
- package/dist/lib/templates/umbrella-chart.d.ts +27 -0
- package/dist/lib/templates/umbrella-chart.js +89 -0
- package/dist/lib/types.d.ts +26 -0
- package/dist/lib/umbrella.d.ts +23 -0
- package/dist/lib/umbrella.js +23 -0
- package/dist/lib/umbrellaRutter.d.ts +75 -0
- package/dist/lib/umbrellaRutter.js +82 -2
- package/dist/lib/utils/envVarsLoader.d.ts +49 -0
- package/dist/lib/utils/envVarsLoader.js +53 -0
- package/dist/lib/utils/helmConstructSerializer.d.ts +17 -0
- package/dist/lib/utils/helmConstructSerializer.js +22 -0
- package/dist/lib/utils/helmControlStructures.d.ts +194 -0
- package/dist/lib/utils/helmControlStructures.js +180 -0
- package/dist/lib/utils/helmHelpers/envHelpers.d.ts +13 -0
- package/dist/lib/utils/helmHelpers/envHelpers.js +13 -0
- package/dist/lib/utils/helmHelpers/gitopsHelpers.d.ts +13 -0
- package/dist/lib/utils/helmHelpers/gitopsHelpers.js +13 -0
- package/dist/lib/utils/helmHelpers/index.d.ts +74 -0
- package/dist/lib/utils/helmHelpers/index.js +85 -1
- package/dist/lib/utils/helmHelpers/observabilityHelpers.d.ts +13 -0
- package/dist/lib/utils/helmHelpers/observabilityHelpers.js +13 -0
- package/dist/lib/utils/helmHelpers/types.d.ts +23 -0
- package/dist/lib/utils/helmHelpers/types.js +4 -0
- package/dist/lib/utils/helmHelpers/validationHelpers.d.ts +13 -0
- package/dist/lib/utils/helmHelpers/validationHelpers.js +13 -0
- package/dist/lib/utils/helmHelpers.d.ts +62 -0
- package/dist/lib/utils/helmHelpers.js +77 -0
- package/dist/lib/utils/helmYamlSerializer.d.ts +77 -0
- package/dist/lib/utils/helmYamlSerializer.js +398 -21
- package/dist/lib/utils/logger.d.ts +153 -0
- package/dist/lib/utils/logger.js +170 -2
- package/dist/lib/utils/valuesRef.d.ts +181 -50
- package/dist/lib/utils/valuesRef.js +168 -170
- package/dist/lib/validation/inputValidator.d.ts +45 -0
- package/dist/lib/validation/inputValidator.js +67 -2
- package/dist/types/index.d.ts +34 -0
- package/dist/types/index.js +3 -0
- 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.`;
|