timonel 3.0.0 → 3.1.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 (40) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +432 -1
  3. package/SECURITY.md +2 -2
  4. package/dist/index.d.ts +3 -0
  5. package/dist/index.js +2 -0
  6. package/dist/lib/policy/configurationLoader.d.ts +46 -0
  7. package/dist/lib/policy/configurationLoader.js +251 -0
  8. package/dist/lib/policy/errorContextGenerator.d.ts +63 -0
  9. package/dist/lib/policy/errorContextGenerator.js +302 -0
  10. package/dist/lib/policy/errors.d.ts +40 -0
  11. package/dist/lib/policy/errors.js +109 -0
  12. package/dist/lib/policy/index.d.ts +11 -0
  13. package/dist/lib/policy/index.js +10 -0
  14. package/dist/lib/policy/parallelExecutor.d.ts +58 -0
  15. package/dist/lib/policy/parallelExecutor.js +215 -0
  16. package/dist/lib/policy/pluginLoader.d.ts +41 -0
  17. package/dist/lib/policy/pluginLoader.js +220 -0
  18. package/dist/lib/policy/pluginRegistry.d.ts +14 -0
  19. package/dist/lib/policy/pluginRegistry.js +69 -0
  20. package/dist/lib/policy/policyEngine.d.ts +39 -0
  21. package/dist/lib/policy/policyEngine.js +495 -0
  22. package/dist/lib/policy/resultAggregator.d.ts +8 -0
  23. package/dist/lib/policy/resultAggregator.js +138 -0
  24. package/dist/lib/policy/resultFormatter.d.ts +25 -0
  25. package/dist/lib/policy/resultFormatter.js +217 -0
  26. package/dist/lib/policy/types.d.ts +111 -0
  27. package/dist/lib/policy/types.js +1 -0
  28. package/dist/lib/policy/validationCache.d.ts +58 -0
  29. package/dist/lib/policy/validationCache.js +289 -0
  30. package/dist/lib/rutter.d.ts +7 -2
  31. package/dist/lib/rutter.js +177 -8
  32. package/dist/lib/templates/flexible-subchart.js +4 -2
  33. package/dist/lib/templates/umbrella-chart.js +8 -8
  34. package/dist/lib/umbrellaRutter.d.ts +1 -1
  35. package/dist/lib/umbrellaRutter.js +2 -2
  36. package/dist/lib/validation/inputValidator.d.ts +26 -0
  37. package/dist/lib/validation/inputValidator.js +176 -0
  38. package/dist/types/index.d.ts +27 -0
  39. package/dist/types/index.js +1 -0
  40. package/package.json +22 -20
@@ -0,0 +1,251 @@
1
+ import { createLogger } from '../utils/logger.js';
2
+ import { PluginConfigurationError } from './errors.js';
3
+ const DEFAULT_LOADER_OPTIONS = {
4
+ defaultEnvironment: 'development',
5
+ validateSchemas: true,
6
+ allowUnknownProperties: false,
7
+ configurationFiles: [],
8
+ environmentPrefix: 'TIMONEL_POLICY',
9
+ };
10
+ const CONFIGURATION_PRIORITIES = {
11
+ default: 0,
12
+ file: 10,
13
+ environment: 20,
14
+ inline: 30,
15
+ };
16
+ export class ConfigurationLoader {
17
+ constructor(options = {}) {
18
+ this.configurations = new Map();
19
+ this.environmentConfigs = new Map();
20
+ this.options = { ...DEFAULT_LOADER_OPTIONS, ...options };
21
+ this.logger = createLogger('policy-config-loader');
22
+ this.logger.debug('ConfigurationLoader initialized', {
23
+ options: this.options,
24
+ operation: 'config_loader_init',
25
+ });
26
+ }
27
+ async loadPluginConfiguration(plugin, environment, inlineConfig) {
28
+ const targetEnvironment = environment || this.options.defaultEnvironment;
29
+ this.logger.debug('Loading plugin configuration', {
30
+ pluginName: plugin.name,
31
+ environment: targetEnvironment,
32
+ hasInlineConfig: inlineConfig !== undefined,
33
+ operation: 'load_plugin_config',
34
+ });
35
+ const entries = await this.collectConfigurationEntries(plugin, targetEnvironment, inlineConfig);
36
+ const mergedConfig = this.mergeConfigurations(entries);
37
+ const validationResult = this.validatePluginConfiguration(plugin, mergedConfig);
38
+ const result = {
39
+ pluginName: plugin.name,
40
+ config: mergedConfig,
41
+ entries,
42
+ validated: validationResult.validated,
43
+ ...(validationResult.validationErrors && {
44
+ validationErrors: validationResult.validationErrors,
45
+ }),
46
+ };
47
+ this.logger.info('Plugin configuration loaded', {
48
+ pluginName: plugin.name,
49
+ environment: targetEnvironment,
50
+ entryCount: entries.length,
51
+ validated: validationResult.validated,
52
+ hasErrors: validationResult.validationErrors !== undefined,
53
+ operation: 'plugin_config_loaded',
54
+ });
55
+ return result;
56
+ }
57
+ async collectConfigurationEntries(plugin, targetEnvironment, inlineConfig) {
58
+ const entries = [];
59
+ if (plugin.metadata?.defaultConfig) {
60
+ entries.push({
61
+ value: plugin.metadata.defaultConfig,
62
+ source: 'default',
63
+ priority: CONFIGURATION_PRIORITIES.default,
64
+ ...(plugin.configSchema && { schema: plugin.configSchema }),
65
+ });
66
+ }
67
+ const fileConfig = await this.loadFileConfiguration(plugin.name, targetEnvironment);
68
+ if (fileConfig) {
69
+ entries.push({
70
+ value: fileConfig,
71
+ source: 'file',
72
+ environment: targetEnvironment,
73
+ priority: CONFIGURATION_PRIORITIES.file,
74
+ ...(plugin.configSchema && { schema: plugin.configSchema }),
75
+ });
76
+ }
77
+ const envConfig = this.loadEnvironmentVariableConfiguration(plugin.name);
78
+ if (envConfig) {
79
+ entries.push({
80
+ value: envConfig,
81
+ source: 'environment',
82
+ priority: CONFIGURATION_PRIORITIES.environment,
83
+ ...(plugin.configSchema && { schema: plugin.configSchema }),
84
+ });
85
+ }
86
+ if (inlineConfig) {
87
+ entries.push({
88
+ value: inlineConfig,
89
+ source: 'inline',
90
+ priority: CONFIGURATION_PRIORITIES.inline,
91
+ ...(plugin.configSchema && { schema: plugin.configSchema }),
92
+ });
93
+ }
94
+ return entries;
95
+ }
96
+ validatePluginConfiguration(plugin, mergedConfig) {
97
+ let validated = false;
98
+ let validationErrors;
99
+ if (this.options.validateSchemas && plugin.configSchema) {
100
+ try {
101
+ this.validateConfiguration(mergedConfig, plugin.configSchema, plugin.name);
102
+ validated = true;
103
+ }
104
+ catch (error) {
105
+ validationErrors = [error instanceof Error ? error.message : String(error)];
106
+ this.logger.warn('Plugin configuration validation failed', {
107
+ pluginName: plugin.name,
108
+ errors: validationErrors,
109
+ operation: 'config_validation_failed',
110
+ });
111
+ }
112
+ }
113
+ return { validated, ...(validationErrors && { validationErrors }) };
114
+ }
115
+ async loadEnvironmentConfiguration(environment) {
116
+ if (this.environmentConfigs.has(environment)) {
117
+ return this.environmentConfigs.get(environment);
118
+ }
119
+ this.logger.debug('Loading environment configuration', {
120
+ environment,
121
+ operation: 'load_env_config',
122
+ });
123
+ const envConfig = await this.loadEnvironmentFromFiles(environment);
124
+ if (envConfig) {
125
+ this.environmentConfigs.set(environment, envConfig);
126
+ this.logger.info('Environment configuration loaded', {
127
+ environment,
128
+ pluginCount: Object.keys(envConfig.plugins).length,
129
+ hasGlobal: envConfig.global !== undefined,
130
+ operation: 'env_config_loaded',
131
+ });
132
+ }
133
+ return envConfig;
134
+ }
135
+ validateConfiguration(config, schema, pluginName) {
136
+ try {
137
+ this.validateAgainstSchema(config, schema, []);
138
+ }
139
+ catch (error) {
140
+ throw new PluginConfigurationError(`Configuration validation failed for plugin '${pluginName}': ${error instanceof Error ? error.message : String(error)}`, pluginName, undefined, { config, schema, validationError: error });
141
+ }
142
+ }
143
+ addConfigurationEntry(pluginName, entry) {
144
+ if (!this.configurations.has(pluginName)) {
145
+ this.configurations.set(pluginName, []);
146
+ }
147
+ const entries = this.configurations.get(pluginName);
148
+ entries.push(entry);
149
+ entries.sort((a, b) => b.priority - a.priority);
150
+ this.logger.debug('Configuration entry added', {
151
+ pluginName,
152
+ source: entry.source,
153
+ priority: entry.priority,
154
+ operation: 'config_entry_added',
155
+ });
156
+ }
157
+ getConfigurationEntries(pluginName) {
158
+ return this.configurations.get(pluginName) || [];
159
+ }
160
+ clearCache() {
161
+ this.configurations.clear();
162
+ this.environmentConfigs.clear();
163
+ this.logger.debug('Configuration cache cleared', {
164
+ operation: 'config_cache_cleared',
165
+ });
166
+ }
167
+ async loadFileConfiguration(_pluginName, _environment) {
168
+ return undefined;
169
+ }
170
+ loadEnvironmentVariableConfiguration(pluginName) {
171
+ if (!pluginName) {
172
+ return undefined;
173
+ }
174
+ const prefix = `${this.options.environmentPrefix}_${pluginName.toUpperCase().replace(/[^A-Z0-9]/g, '_')}`;
175
+ const config = Object.create(null);
176
+ let hasConfig = false;
177
+ for (const [key, value] of Object.entries(process.env)) {
178
+ if (key.startsWith(prefix + '_')) {
179
+ const configKey = key.substring(prefix.length + 1).toLowerCase();
180
+ config[configKey] = this.parseEnvironmentValue(value);
181
+ hasConfig = true;
182
+ }
183
+ }
184
+ return hasConfig ? config : undefined;
185
+ }
186
+ async loadEnvironmentFromFiles(_environment) {
187
+ return undefined;
188
+ }
189
+ mergeConfigurations(entries) {
190
+ const sortedEntries = [...entries].sort((a, b) => a.priority - b.priority);
191
+ let merged = {};
192
+ for (const entry of sortedEntries) {
193
+ if (entry.value && typeof entry.value === 'object' && !Array.isArray(entry.value)) {
194
+ merged = { ...merged, ...entry.value };
195
+ }
196
+ }
197
+ return merged;
198
+ }
199
+ parseEnvironmentValue(value) {
200
+ if (!value)
201
+ return undefined;
202
+ try {
203
+ return JSON.parse(value);
204
+ }
205
+ catch {
206
+ return value;
207
+ }
208
+ }
209
+ validateAgainstSchema(config, schema, path) {
210
+ this.validateBasicType(config, schema, path);
211
+ this.validateObjectProperties(config, schema, path);
212
+ }
213
+ validateBasicType(config, schema, path) {
214
+ if (schema.type) {
215
+ const actualType = Array.isArray(config) ? 'array' : typeof config;
216
+ if (actualType !== schema.type) {
217
+ throw new Error(`Expected ${schema.type} at ${path.join('.')}, got ${actualType}`);
218
+ }
219
+ }
220
+ }
221
+ validateObjectProperties(config, schema, path) {
222
+ if (schema.type === 'object' &&
223
+ schema.properties &&
224
+ typeof config === 'object' &&
225
+ config !== null) {
226
+ const configObj = config;
227
+ this.validateRequiredProperties(configObj, schema, path);
228
+ this.validateEachProperty(configObj, schema, path);
229
+ }
230
+ }
231
+ validateRequiredProperties(configObj, schema, path) {
232
+ if (schema.required) {
233
+ for (const requiredProp of schema.required) {
234
+ if (!(requiredProp in configObj)) {
235
+ throw new Error(`Missing required property '${requiredProp}' at ${path.join('.')}`);
236
+ }
237
+ }
238
+ }
239
+ }
240
+ validateEachProperty(configObj, schema, path) {
241
+ for (const [propName, propValue] of Object.entries(configObj)) {
242
+ const propSchema = schema.properties?.[propName];
243
+ if (propSchema) {
244
+ this.validateAgainstSchema(propValue, propSchema, [...path, propName]);
245
+ }
246
+ else if (!this.options.allowUnknownProperties && !schema.additionalProperties) {
247
+ throw new Error(`Unknown property '${propName}' at ${path.join('.')}`);
248
+ }
249
+ }
250
+ }
251
+ }
@@ -0,0 +1,63 @@
1
+ import type { PolicyViolation, PolicyWarning, ValidationContext } from './types.js';
2
+ export interface ErrorContext {
3
+ readonly timestamp: string;
4
+ readonly environment: {
5
+ readonly nodeVersion: string;
6
+ readonly platform: string;
7
+ readonly arch: string;
8
+ readonly timonelVersion?: string;
9
+ };
10
+ readonly validationContext: {
11
+ readonly chartName?: string;
12
+ readonly chartVersion?: string;
13
+ readonly kubernetesVersion?: string;
14
+ readonly environment?: string;
15
+ };
16
+ readonly plugin: {
17
+ readonly name: string;
18
+ readonly version?: string;
19
+ readonly executionTime?: number;
20
+ };
21
+ readonly error: {
22
+ readonly message: string;
23
+ readonly severity: 'error' | 'warning' | 'info';
24
+ readonly resourcePath?: string;
25
+ readonly field?: string;
26
+ readonly suggestion?: string;
27
+ readonly stackTrace?: string;
28
+ readonly originalContext?: Record<string, unknown>;
29
+ };
30
+ readonly debuggingHints: string[];
31
+ readonly relatedViolations?: Array<{
32
+ readonly plugin: string;
33
+ readonly message: string;
34
+ readonly similarity: number;
35
+ }>;
36
+ }
37
+ export declare class ErrorContextGenerator {
38
+ generateContext(violation: PolicyViolation | PolicyWarning, validationContext?: ValidationContext, allViolations?: (PolicyViolation | PolicyWarning)[], executionTime?: number): ErrorContext;
39
+ generateErrorReport(violation: PolicyViolation | PolicyWarning, validationContext?: ValidationContext, allViolations?: (PolicyViolation | PolicyWarning)[], executionTime?: number): string;
40
+ private addReportHeader;
41
+ private addBasicInformation;
42
+ private addResourceInformation;
43
+ private addSuggestion;
44
+ private addEnvironmentInfo;
45
+ private addValidationContextInfo;
46
+ private addDebuggingHints;
47
+ private addRelatedViolations;
48
+ private addOriginalContext;
49
+ private getEnvironmentInfo;
50
+ private getTimonelVersion;
51
+ private getValidationContextInfo;
52
+ private generateDebuggingHints;
53
+ private addSeverityHints;
54
+ private addResourcePathHints;
55
+ private addFieldHints;
56
+ private addPluginHints;
57
+ private addEnvironmentHints;
58
+ private addKubernetesVersionHints;
59
+ private findRelatedViolations;
60
+ private calculateSimilarity;
61
+ private calculateStringSimilarity;
62
+ private calculateLevenshteinDistance;
63
+ }
@@ -0,0 +1,302 @@
1
+ export class ErrorContextGenerator {
2
+ generateContext(violation, validationContext, allViolations, executionTime) {
3
+ return {
4
+ timestamp: new Date().toISOString(),
5
+ environment: this.getEnvironmentInfo(),
6
+ validationContext: this.getValidationContextInfo(validationContext),
7
+ plugin: {
8
+ name: violation.plugin,
9
+ ...(executionTime !== undefined && { executionTime }),
10
+ },
11
+ error: {
12
+ message: violation.message,
13
+ severity: violation.severity,
14
+ ...(violation.resourcePath && { resourcePath: violation.resourcePath }),
15
+ ...(violation.field && { field: violation.field }),
16
+ ...(violation.suggestion && { suggestion: violation.suggestion }),
17
+ ...(violation.context && { originalContext: violation.context }),
18
+ },
19
+ debuggingHints: this.generateDebuggingHints(violation, validationContext),
20
+ relatedViolations: this.findRelatedViolations(violation, allViolations),
21
+ };
22
+ }
23
+ generateErrorReport(violation, validationContext, allViolations, executionTime) {
24
+ const context = this.generateContext(violation, validationContext, allViolations, executionTime);
25
+ const lines = [];
26
+ this.addReportHeader(lines, violation);
27
+ this.addBasicInformation(lines, context);
28
+ this.addResourceInformation(lines, context);
29
+ this.addSuggestion(lines, context);
30
+ this.addEnvironmentInfo(lines, context);
31
+ this.addValidationContextInfo(lines, context);
32
+ this.addDebuggingHints(lines, context);
33
+ this.addRelatedViolations(lines, context);
34
+ this.addOriginalContext(lines, context);
35
+ return lines.join('\n');
36
+ }
37
+ addReportHeader(lines, violation) {
38
+ lines.push('='.repeat(80));
39
+ lines.push(`Policy Violation Report - ${violation.severity.toUpperCase()}`);
40
+ lines.push('='.repeat(80));
41
+ lines.push('');
42
+ }
43
+ addBasicInformation(lines, context) {
44
+ lines.push('Basic Information:');
45
+ lines.push(` Plugin: ${context.plugin.name}`);
46
+ lines.push(` Severity: ${context.error.severity}`);
47
+ lines.push(` Message: ${context.error.message}`);
48
+ lines.push(` Timestamp: ${context.timestamp}`);
49
+ if (context.plugin.executionTime !== undefined) {
50
+ lines.push(` Exec Time: ${context.plugin.executionTime}ms`);
51
+ }
52
+ lines.push('');
53
+ }
54
+ addResourceInformation(lines, context) {
55
+ if (context.error.resourcePath || context.error.field) {
56
+ lines.push('Resource Information:');
57
+ if (context.error.resourcePath) {
58
+ lines.push(` Path: ${context.error.resourcePath}`);
59
+ }
60
+ if (context.error.field) {
61
+ lines.push(` Field: ${context.error.field}`);
62
+ }
63
+ lines.push('');
64
+ }
65
+ }
66
+ addSuggestion(lines, context) {
67
+ if (context.error.suggestion) {
68
+ lines.push('Suggested Fix:');
69
+ lines.push(` ${context.error.suggestion}`);
70
+ lines.push('');
71
+ }
72
+ }
73
+ addEnvironmentInfo(lines, context) {
74
+ lines.push('Environment:');
75
+ lines.push(` Node.js: ${context.environment.nodeVersion}`);
76
+ lines.push(` Platform: ${context.environment.platform} (${context.environment.arch})`);
77
+ if (context.environment.timonelVersion) {
78
+ lines.push(` Timonel: ${context.environment.timonelVersion}`);
79
+ }
80
+ lines.push('');
81
+ }
82
+ addValidationContextInfo(lines, context) {
83
+ const hasValidationContext = context.validationContext.chartName ||
84
+ context.validationContext.kubernetesVersion ||
85
+ context.validationContext.environment;
86
+ if (hasValidationContext) {
87
+ lines.push('Validation Context:');
88
+ if (context.validationContext.chartName) {
89
+ const version = context.validationContext.chartVersion || 'unknown';
90
+ lines.push(` Chart: ${context.validationContext.chartName}@${version}`);
91
+ }
92
+ if (context.validationContext.kubernetesVersion) {
93
+ lines.push(` Kubernetes: ${context.validationContext.kubernetesVersion}`);
94
+ }
95
+ if (context.validationContext.environment) {
96
+ lines.push(` Environment: ${context.validationContext.environment}`);
97
+ }
98
+ lines.push('');
99
+ }
100
+ }
101
+ addDebuggingHints(lines, context) {
102
+ if (context.debuggingHints.length > 0) {
103
+ lines.push('Debugging Hints:');
104
+ context.debuggingHints.forEach((hint) => {
105
+ lines.push(` • ${hint}`);
106
+ });
107
+ lines.push('');
108
+ }
109
+ }
110
+ addRelatedViolations(lines, context) {
111
+ if (context.relatedViolations && context.relatedViolations.length > 0) {
112
+ lines.push('Related Violations:');
113
+ for (const related of context.relatedViolations) {
114
+ lines.push(` • [${related.plugin}] ${related.message} (${Math.round(related.similarity * 100)}% similar)`);
115
+ }
116
+ lines.push('');
117
+ }
118
+ }
119
+ addOriginalContext(lines, context) {
120
+ if (context.error.originalContext && Object.keys(context.error.originalContext).length > 0) {
121
+ lines.push('Additional Context:');
122
+ lines.push(JSON.stringify(context.error.originalContext, null, 2)
123
+ .split('\n')
124
+ .map((line) => ` ${line}`)
125
+ .join('\n'));
126
+ lines.push('');
127
+ }
128
+ }
129
+ getEnvironmentInfo() {
130
+ const timonelVersion = this.getTimonelVersion();
131
+ return {
132
+ nodeVersion: process.version,
133
+ platform: process.platform,
134
+ arch: process.arch,
135
+ ...(timonelVersion && { timonelVersion }),
136
+ };
137
+ }
138
+ getTimonelVersion() {
139
+ return '3.0.0';
140
+ }
141
+ getValidationContextInfo(validationContext) {
142
+ return {
143
+ ...(validationContext?.chart?.name && { chartName: validationContext.chart.name }),
144
+ ...(validationContext?.chart?.version && { chartVersion: validationContext.chart.version }),
145
+ ...(validationContext?.kubernetesVersion && {
146
+ kubernetesVersion: validationContext.kubernetesVersion,
147
+ }),
148
+ ...(validationContext?.environment && { environment: validationContext.environment }),
149
+ };
150
+ }
151
+ generateDebuggingHints(violation, validationContext) {
152
+ const hints = [];
153
+ this.addSeverityHints(hints, violation);
154
+ this.addResourcePathHints(hints, violation);
155
+ this.addFieldHints(hints, violation);
156
+ this.addPluginHints(hints, violation);
157
+ this.addEnvironmentHints(hints, validationContext);
158
+ this.addKubernetesVersionHints(hints, validationContext);
159
+ return hints;
160
+ }
161
+ addSeverityHints(hints, violation) {
162
+ if (violation.severity === 'error') {
163
+ hints.push('This error will prevent chart generation. Fix this violation to proceed.');
164
+ }
165
+ else if (violation.severity === 'warning') {
166
+ hints.push("This warning indicates a potential issue but won't block chart generation.");
167
+ }
168
+ }
169
+ addResourcePathHints(hints, violation) {
170
+ if (!violation.resourcePath)
171
+ return;
172
+ if (violation.resourcePath.includes('spec.containers')) {
173
+ hints.push('This violation is related to container specifications. Check container configuration.');
174
+ }
175
+ else if (violation.resourcePath.includes('metadata')) {
176
+ hints.push('This violation is related to resource metadata. Verify labels, annotations, and names.');
177
+ }
178
+ else if (violation.resourcePath.includes('spec.template')) {
179
+ hints.push('This violation is in a pod template. Check deployment or statefulset configuration.');
180
+ }
181
+ }
182
+ addFieldHints(hints, violation) {
183
+ if (!violation.field)
184
+ return;
185
+ if (violation.field.includes('securityContext')) {
186
+ hints.push('Security context violations often relate to privilege escalation or root access.');
187
+ }
188
+ else if (violation.field.includes('resources')) {
189
+ hints.push('Resource violations typically involve CPU/memory limits or requests.');
190
+ }
191
+ else if (violation.field.includes('image')) {
192
+ hints.push('Image violations may relate to image tags, registries, or security policies.');
193
+ }
194
+ }
195
+ addPluginHints(hints, violation) {
196
+ if (violation.plugin.includes('security')) {
197
+ hints.push('Security violations should be addressed promptly to maintain cluster security.');
198
+ hints.push("Consider reviewing your organization's security policies and best practices.");
199
+ }
200
+ else if (violation.plugin.includes('resource')) {
201
+ hints.push('Resource violations can impact cluster performance and cost.');
202
+ }
203
+ else if (violation.plugin.includes('network')) {
204
+ hints.push('Network violations may affect service connectivity and security.');
205
+ }
206
+ }
207
+ addEnvironmentHints(hints, validationContext) {
208
+ if (validationContext?.environment === 'production') {
209
+ hints.push('This is a production environment - ensure all violations are resolved before deployment.');
210
+ }
211
+ else if (validationContext?.environment === 'development') {
212
+ hints.push('Development environment detected - some violations may be acceptable for testing.');
213
+ }
214
+ }
215
+ addKubernetesVersionHints(hints, validationContext) {
216
+ if (validationContext?.kubernetesVersion) {
217
+ const version = validationContext.kubernetesVersion;
218
+ if (version.startsWith('1.2')) {
219
+ hints.push('Kubernetes 1.2x detected - ensure compatibility with newer API versions.');
220
+ }
221
+ }
222
+ }
223
+ findRelatedViolations(violation, allViolations) {
224
+ if (!allViolations || allViolations.length <= 1) {
225
+ return [];
226
+ }
227
+ const related = [];
228
+ for (const other of allViolations) {
229
+ if (other === violation)
230
+ continue;
231
+ const similarity = this.calculateSimilarity(violation, other);
232
+ if (similarity > 0.3) {
233
+ related.push({
234
+ plugin: other.plugin,
235
+ message: other.message,
236
+ similarity,
237
+ });
238
+ }
239
+ }
240
+ return related.sort((a, b) => b.similarity - a.similarity).slice(0, 3);
241
+ }
242
+ calculateSimilarity(violation1, violation2) {
243
+ let score = 0;
244
+ let factors = 0;
245
+ if (violation1.plugin === violation2.plugin) {
246
+ score += 0.4;
247
+ }
248
+ factors += 0.4;
249
+ if (violation1.severity === violation2.severity) {
250
+ score += 0.2;
251
+ }
252
+ factors += 0.2;
253
+ if (violation1.resourcePath && violation2.resourcePath) {
254
+ const pathSimilarity = this.calculateStringSimilarity(violation1.resourcePath, violation2.resourcePath);
255
+ score += pathSimilarity * 0.2;
256
+ }
257
+ factors += 0.2;
258
+ if (violation1.field && violation2.field) {
259
+ const fieldSimilarity = this.calculateStringSimilarity(violation1.field, violation2.field);
260
+ score += fieldSimilarity * 0.1;
261
+ }
262
+ factors += 0.1;
263
+ const messageSimilarity = this.calculateStringSimilarity(violation1.message, violation2.message);
264
+ score += messageSimilarity * 0.1;
265
+ factors += 0.1;
266
+ return factors > 0 ? score / factors : 0;
267
+ }
268
+ calculateStringSimilarity(str1, str2) {
269
+ if (str1 === str2)
270
+ return 1;
271
+ if (str1.length === 0 || str2.length === 0)
272
+ return 0;
273
+ const longer = str1.length > str2.length ? str1 : str2;
274
+ const shorter = str1.length > str2.length ? str2 : str1;
275
+ if (longer.length === 0)
276
+ return 1;
277
+ const editDistance = this.calculateLevenshteinDistance(longer, shorter);
278
+ return (longer.length - editDistance) / longer.length;
279
+ }
280
+ calculateLevenshteinDistance(str1, str2) {
281
+ const matrix = Array(str2.length + 1)
282
+ .fill(null)
283
+ .map(() => Array(str1.length + 1).fill(0));
284
+ for (let i = 0; i <= str2.length; i++) {
285
+ matrix[i][0] = i;
286
+ }
287
+ for (let j = 0; j <= str1.length; j++) {
288
+ matrix[0][j] = j;
289
+ }
290
+ for (let i = 1; i <= str2.length; i++) {
291
+ for (let j = 1; j <= str1.length; j++) {
292
+ if (str2.charAt(i - 1) === str1.charAt(j - 1)) {
293
+ matrix[i][j] = matrix[i - 1][j - 1];
294
+ }
295
+ else {
296
+ matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j] + 1);
297
+ }
298
+ }
299
+ }
300
+ return matrix[str2.length][str1.length];
301
+ }
302
+ }
@@ -0,0 +1,40 @@
1
+ export declare class PolicyEngineError extends Error {
2
+ readonly code?: string | undefined;
3
+ readonly context?: Record<string, unknown> | undefined;
4
+ constructor(message: string, code?: string | undefined, context?: Record<string, unknown> | undefined);
5
+ }
6
+ export declare class PluginError extends PolicyEngineError {
7
+ readonly pluginName: string;
8
+ constructor(message: string, pluginName: string, context?: Record<string, unknown>);
9
+ }
10
+ export declare class ValidationTimeoutError extends PolicyEngineError {
11
+ constructor(pluginName: string, timeout: number);
12
+ }
13
+ export declare class PluginRegistrationError extends PolicyEngineError {
14
+ readonly pluginName?: string | undefined;
15
+ constructor(message: string, pluginName?: string | undefined, context?: Record<string, unknown>);
16
+ }
17
+ export declare class PluginConfigurationError extends PolicyEngineError {
18
+ readonly pluginName: string;
19
+ readonly configPath?: string | undefined;
20
+ constructor(message: string, pluginName: string, configPath?: string | undefined, context?: Record<string, unknown>);
21
+ }
22
+ export declare class ValidationOrchestrationError extends PolicyEngineError {
23
+ readonly failedPlugins?: string[] | undefined;
24
+ constructor(message: string, failedPlugins?: string[] | undefined, context?: Record<string, unknown>);
25
+ }
26
+ export declare class PluginRetryExhaustedError extends PolicyEngineError {
27
+ readonly pluginName: string;
28
+ readonly attempts: number;
29
+ readonly lastError: Error;
30
+ constructor(message: string, pluginName: string, attempts: number, lastError: Error, context?: Record<string, unknown>);
31
+ }
32
+ export declare class GracefulDegradationError extends PolicyEngineError {
33
+ readonly degradedPlugins: string[];
34
+ readonly originalErrors: Error[];
35
+ constructor(message: string, degradedPlugins: string[], originalErrors: Error[], context?: Record<string, unknown>);
36
+ }
37
+ export declare class PolicyValidationError extends PolicyEngineError {
38
+ readonly field?: string | undefined;
39
+ constructor(message: string, field?: string | undefined, context?: Record<string, unknown>);
40
+ }