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,4 +1,12 @@
1
+ /**
2
+ * Input validation utilities for the Policy Engine
3
+ * Provides comprehensive validation for all user inputs to prevent
4
+ * injection attacks and malformed data processing
5
+ */
1
6
  import { PolicyValidationError } from '../policy/errors.js';
7
+ /**
8
+ * Default validation options
9
+ */
2
10
  const DEFAULT_OPTIONS = {
3
11
  maxStringLength: 10000,
4
12
  maxArrayLength: 1000,
@@ -6,15 +14,22 @@ const DEFAULT_OPTIONS = {
6
14
  allowedProtocols: ['http', 'https'],
7
15
  sanitizeStrings: true,
8
16
  };
17
+ /**
18
+ * Input validator class for policy engine inputs
19
+ */
9
20
  export class InputValidator {
10
21
  constructor(options = {}) {
11
22
  this.options = { ...DEFAULT_OPTIONS, ...options };
12
23
  }
24
+ /**
25
+ * Validates a PolicyConfig object
26
+ */
13
27
  validatePolicyConfig(config) {
14
28
  if (!config || typeof config !== 'object') {
15
29
  throw new PolicyValidationError('PolicyConfig must be a non-null object');
16
30
  }
17
31
  const typedConfig = config;
32
+ // Validate required fields
18
33
  if (!typedConfig.id || typeof typedConfig.id !== 'string') {
19
34
  throw new PolicyValidationError('PolicyConfig.id must be a non-empty string');
20
35
  }
@@ -24,9 +39,12 @@ export class InputValidator {
24
39
  if (!Array.isArray(typedConfig.rules)) {
25
40
  throw new PolicyValidationError('PolicyConfig.rules must be an array');
26
41
  }
42
+ // Validate string lengths
27
43
  this.validateStringLength(typedConfig.id, 'PolicyConfig.id');
28
44
  this.validateStringLength(typedConfig.name, 'PolicyConfig.name');
45
+ // Validate array length
29
46
  this.validateArrayLength(typedConfig.rules, 'PolicyConfig.rules');
47
+ // Validate each rule
30
48
  const validatedRules = typedConfig.rules.map((rule, index) => {
31
49
  try {
32
50
  return this.validatePolicyRule(rule);
@@ -35,6 +53,7 @@ export class InputValidator {
35
53
  throw new PolicyValidationError(`PolicyConfig.rules[${index}]: ${error.message}`);
36
54
  }
37
55
  });
56
+ // Sanitize strings if enabled
38
57
  const sanitizedConfig = {
39
58
  id: this.sanitizeString(typedConfig.id),
40
59
  name: this.sanitizeString(typedConfig.name),
@@ -49,11 +68,15 @@ export class InputValidator {
49
68
  }
50
69
  return sanitizedConfig;
51
70
  }
71
+ /**
72
+ * Validates a PolicyRule object
73
+ */
52
74
  validatePolicyRule(rule) {
53
75
  if (!rule || typeof rule !== 'object') {
54
76
  throw new PolicyValidationError('PolicyRule must be a non-null object');
55
77
  }
56
78
  const typedRule = rule;
79
+ // Validate required fields
57
80
  if (!typedRule.id || typeof typedRule.id !== 'string') {
58
81
  throw new PolicyValidationError('PolicyRule.id must be a non-empty string');
59
82
  }
@@ -63,8 +86,10 @@ export class InputValidator {
63
86
  if (!typedRule.condition || typeof typedRule.condition !== 'object') {
64
87
  throw new PolicyValidationError('PolicyRule.condition must be an object');
65
88
  }
89
+ // Validate string lengths
66
90
  this.validateStringLength(typedRule.id, 'PolicyRule.id');
67
91
  this.validateStringLength(typedRule.type, 'PolicyRule.type');
92
+ // Validate object depth
68
93
  this.validateObjectDepth(typedRule.condition, 'PolicyRule.condition');
69
94
  const sanitizedRule = {
70
95
  id: this.sanitizeString(typedRule.id),
@@ -78,23 +103,34 @@ export class InputValidator {
78
103
  }
79
104
  return sanitizedRule;
80
105
  }
106
+ /**
107
+ * Validates a PolicyContext object
108
+ */
81
109
  validatePolicyContext(context) {
82
110
  if (!context || typeof context !== 'object') {
83
111
  throw new PolicyValidationError('PolicyContext must be a non-null object');
84
112
  }
85
113
  const typedContext = context;
114
+ // Validate object depth
86
115
  this.validateObjectDepth(typedContext, 'PolicyContext');
116
+ // Sanitize the entire context object
87
117
  return this.sanitizeObject(typedContext);
88
118
  }
119
+ /**
120
+ * Validates a PluginConfig object
121
+ */
89
122
  validatePluginConfig(config) {
90
123
  if (!config || typeof config !== 'object') {
91
124
  throw new PolicyValidationError('PluginConfig must be a non-null object');
92
125
  }
93
126
  const typedConfig = config;
127
+ // Validate required fields
94
128
  if (!typedConfig.name || typeof typedConfig.name !== 'string') {
95
129
  throw new PolicyValidationError('PluginConfig.name must be a non-empty string');
96
130
  }
131
+ // Validate string lengths
97
132
  this.validateStringLength(typedConfig.name, 'PluginConfig.name');
133
+ // Validate configuration object if present
98
134
  if (typedConfig.config && typeof typedConfig.config === 'object') {
99
135
  this.validateObjectDepth(typedConfig.config, 'PluginConfig.config');
100
136
  }
@@ -110,16 +146,25 @@ export class InputValidator {
110
146
  }
111
147
  return sanitizedConfig;
112
148
  }
149
+ /**
150
+ * Validates string length against maximum allowed
151
+ */
113
152
  validateStringLength(value, fieldName) {
114
153
  if (value.length > this.options.maxStringLength) {
115
154
  throw new PolicyValidationError(`${fieldName} exceeds maximum length of ${this.options.maxStringLength} characters`);
116
155
  }
117
156
  }
157
+ /**
158
+ * Validates array length against maximum allowed
159
+ */
118
160
  validateArrayLength(value, fieldName) {
119
161
  if (value.length > this.options.maxArrayLength) {
120
162
  throw new PolicyValidationError(`${fieldName} exceeds maximum length of ${this.options.maxArrayLength} items`);
121
163
  }
122
164
  }
165
+ /**
166
+ * Validates object depth to prevent deeply nested objects
167
+ */
123
168
  validateObjectDepth(obj, fieldName, currentDepth = 0) {
124
169
  if (currentDepth > this.options.maxObjectDepth) {
125
170
  throw new PolicyValidationError(`${fieldName} exceeds maximum object depth of ${this.options.maxObjectDepth}`);
@@ -130,32 +175,45 @@ export class InputValidator {
130
175
  }
131
176
  }
132
177
  }
178
+ /**
179
+ * Sanitizes a string by removing potentially dangerous characters
180
+ * Focuses on policy configuration validation, not HTML sanitization
181
+ */
133
182
  sanitizeString(value) {
134
183
  if (!this.options.sanitizeStrings) {
135
184
  return value;
136
185
  }
186
+ // Remove null bytes and control characters only
137
187
  let sanitized = value
138
- .replace(/\0/g, '')
188
+ .replace(/\0/g, '') // Remove null bytes
139
189
  .split('')
140
190
  .filter((char) => {
141
191
  const code = char.charCodeAt(0);
142
- return code > 31 && code !== 127;
192
+ return code > 31 && code !== 127; // Keep printable characters only
143
193
  })
144
194
  .join('');
195
+ // Remove only genuinely dangerous patterns for policy configurations
196
+ // No HTML sanitization needed - these are policy configs, not web content
145
197
  sanitized = sanitized.trim();
146
198
  return sanitized;
147
199
  }
200
+ /**
201
+ * Sanitizes an object recursively
202
+ */
148
203
  sanitizeObject(obj) {
149
204
  const sanitized = {};
150
205
  for (const [key, value] of Object.entries(obj)) {
151
206
  const sanitizedKey = this.sanitizeString(key);
152
207
  if (typeof value === 'string') {
208
+ // eslint-disable-next-line security/detect-object-injection
153
209
  sanitized[sanitizedKey] = this.sanitizeString(value);
154
210
  }
155
211
  else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
212
+ // eslint-disable-next-line security/detect-object-injection
156
213
  sanitized[sanitizedKey] = this.sanitizeObject(value);
157
214
  }
158
215
  else if (Array.isArray(value)) {
216
+ // eslint-disable-next-line security/detect-object-injection
159
217
  sanitized[sanitizedKey] = value.map((item) => typeof item === 'string'
160
218
  ? this.sanitizeString(item)
161
219
  : typeof item === 'object' && item !== null
@@ -163,13 +221,20 @@ export class InputValidator {
163
221
  : item);
164
222
  }
165
223
  else {
224
+ // eslint-disable-next-line security/detect-object-injection
166
225
  sanitized[sanitizedKey] = value;
167
226
  }
168
227
  }
169
228
  return sanitized;
170
229
  }
171
230
  }
231
+ /**
232
+ * Default input validator instance
233
+ */
172
234
  export const defaultValidator = new InputValidator();
235
+ /**
236
+ * Convenience functions for common validation scenarios
237
+ */
173
238
  export const validatePolicyConfig = (config) => defaultValidator.validatePolicyConfig(config);
174
239
  export const validatePolicyRule = (rule) => defaultValidator.validatePolicyRule(rule);
175
240
  export const validatePolicyContext = (context) => defaultValidator.validatePolicyContext(context);
@@ -1,27 +1,61 @@
1
+ /**
2
+ * Type definitions for the Policy Engine input validation
3
+ */
4
+ /**
5
+ * Configuration for a policy
6
+ */
1
7
  export interface PolicyConfig {
8
+ /** Unique identifier for the policy */
2
9
  id: string;
10
+ /** Human-readable name for the policy */
3
11
  name: string;
12
+ /** Array of policy rules */
4
13
  rules: PolicyRule[];
14
+ /** Optional description of the policy */
5
15
  description?: string;
16
+ /** Version of the policy */
6
17
  version?: string;
18
+ /** Whether the policy is enabled */
7
19
  enabled?: boolean;
8
20
  }
21
+ /**
22
+ * A single policy rule
23
+ */
9
24
  export interface PolicyRule {
25
+ /** Unique identifier for the rule */
10
26
  id: string;
27
+ /** Type of the rule (e.g., 'security', 'compliance') */
11
28
  type: string;
29
+ /** Condition object that defines when the rule applies */
12
30
  condition: Record<string, unknown>;
31
+ /** Optional action to take when rule is violated */
13
32
  action?: string;
33
+ /** Priority of the rule (higher numbers = higher priority) */
14
34
  priority?: number;
35
+ /** Whether the rule is enabled */
15
36
  enabled?: boolean;
16
37
  }
38
+ /**
39
+ * Context data for policy execution
40
+ */
17
41
  export interface PolicyContext {
42
+ /** Environment (e.g., 'production', 'staging') */
18
43
  environment?: string;
44
+ /** Additional metadata */
19
45
  metadata?: Record<string, unknown>;
46
+ /** Any other context data */
20
47
  [key: string]: unknown;
21
48
  }
49
+ /**
50
+ * Configuration for a plugin
51
+ */
22
52
  export interface PluginConfig {
53
+ /** Name of the plugin */
23
54
  name: string;
55
+ /** Version of the plugin */
24
56
  version?: string;
57
+ /** Plugin-specific configuration */
25
58
  config?: Record<string, unknown>;
59
+ /** Whether the plugin is enabled */
26
60
  enabled?: boolean;
27
61
  }
@@ -1 +1,4 @@
1
+ /**
2
+ * Type definitions for the Policy Engine input validation
3
+ */
1
4
  export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "timonel",
3
3
  "type": "module",
4
- "version": "3.1.1",
4
+ "version": "3.1.2",
5
5
  "description": "Timonel: programmatic Helm chart generator using cdk8s (TypeScript)",
6
6
  "bin": {
7
7
  "timonel": "dist/cli.js",
@@ -43,6 +43,7 @@
43
43
  "prepare": "husky",
44
44
  "prepack": "pnpm build",
45
45
  "typecheck": "tsc -p tsconfig.json --noEmit",
46
+ "typecheck:consumer": "tsc -p tests/types/tsconfig.json --noEmit",
46
47
  "lint": "eslint .",
47
48
  "lint:fix": "eslint . --fix",
48
49
  "format": "prettier --write .",
@@ -58,10 +59,10 @@
58
59
  "test:unit": "vitest run --config vitest.config.ts",
59
60
  "test:integration": "vitest run --config vitest.integration.config.ts",
60
61
  "test:watch": "vitest",
61
- "test:coverage": "vitest run --coverage",
62
+ "test:coverage": "vitest run --coverage --exclude tests/performance-benchmarks.test.ts",
62
63
  "doc:coverage": "tsx scripts/project-doc-coverage.ts",
63
64
  "doc:coverage:validate": "tsx scripts/validate-doc-coverage.ts",
64
- "ci:check": "pnpm typecheck && pnpm lint && pnpm format:check && pnpm build",
65
+ "ci:check": "pnpm typecheck && pnpm lint && pnpm format:check && pnpm build && pnpm typecheck:consumer",
65
66
  "release": "semantic-release",
66
67
  "release:dry": "semantic-release --dry-run",
67
68
  "version:patch": "npm version patch && echo 'Remember to update CHANGELOG.md'",
@@ -78,7 +79,7 @@
78
79
  "author": "Franklin García <KenkoGeek>",
79
80
  "license": "MIT",
80
81
  "engines": {
81
- "node": ">=22",
82
+ "node": "^22.22.2 || ^24.15.0 || >=26.0.0",
82
83
  "pnpm": ">=9.0.0"
83
84
  },
84
85
  "repository": {
@@ -102,43 +103,41 @@
102
103
  ]
103
104
  },
104
105
  "dependencies": {
105
- "cdk8s": "^2.70.51",
106
- "cdk8s-plus-33": "^2.4.37",
107
- "constructs": "^10.5.1",
108
- "handlebars": "^4.7.8",
106
+ "cdk8s": "^2.70.100",
107
+ "cdk8s-plus-33": "^2.5.53",
108
+ "constructs": "^10.8.1",
109
+ "handlebars": "^4.7.9",
109
110
  "pino": "^10.3.1",
110
111
  "pino-pretty": "^13.1.3",
111
112
  "ts-node": "^10.9.2",
112
- "yaml": "^2.8.2"
113
+ "yaml": "^2.9.0"
113
114
  },
114
115
  "devDependencies": {
115
- "@commitlint/cli": "^20.5.0",
116
- "@commitlint/config-conventional": "^20.5.0",
116
+ "@commitlint/cli": "^21.2.2",
117
+ "@commitlint/config-conventional": "^21.2.2",
117
118
  "@eslint/js": "^10.0.1",
118
- "@semantic-release/changelog": "^6.0.3",
119
+ "@semantic-release/changelog": "^7.0.0",
119
120
  "@semantic-release/exec": "^7.1.0",
120
- "@semantic-release/git": "^10.0.1",
121
- "@types/node": "^25.5.0",
122
- "@typescript-eslint/eslint-plugin": "^8.57.0",
123
- "@typescript-eslint/parser": "^8.57.0",
124
- "@vitest/coverage-v8": "^4.1.0",
125
- "conventional-changelog-cli": "^5.0.0",
126
- "eslint": "^10.0.3",
121
+ "@semantic-release/git": "^11.0.1",
122
+ "@types/node": "^26.4.1",
123
+ "@typescript-eslint/eslint-plugin": "^8.69.0",
124
+ "@typescript-eslint/parser": "^8.69.0",
125
+ "@vitest/coverage-v8": "^5.0.0",
126
+ "eslint": "^10.10.0",
127
127
  "eslint-config-prettier": "^10.1.8",
128
- "eslint-import-resolver-typescript": "^4.4.4",
129
- "eslint-plugin-import": "^2.32.0",
130
- "eslint-plugin-security": "^4.0.0",
131
- "eslint-plugin-sonarjs": "^4.0.2",
128
+ "eslint-plugin-import-x": "^4.17.1",
129
+ "eslint-plugin-security": "^4.0.1",
130
+ "eslint-plugin-sonarjs": "^4.2.0",
132
131
  "eslint-plugin-unused-imports": "^4.4.1",
133
132
  "husky": "^9.1.7",
134
- "lint-staged": "^16.4.0",
135
- "markdownlint": "^0.40.0",
136
- "markdownlint-cli": "^0.48.0",
137
- "prettier": "^3.8.1",
138
- "semantic-release": "^25.0.3",
139
- "tsx": "^4.21.0",
140
- "typescript": "^5.9.3",
141
- "vitest": "^4.1.0"
133
+ "lint-staged": "^17.4.1",
134
+ "markdownlint": "^0.41.1",
135
+ "markdownlint-cli": "^0.49.1",
136
+ "prettier": "^3.9.6",
137
+ "semantic-release": "^25.0.9",
138
+ "tsx": "^4.23.13",
139
+ "typescript": "^6.0.3",
140
+ "vitest": "^5.0.0"
142
141
  },
143
142
  "files": [
144
143
  "dist",
@@ -151,11 +150,5 @@
151
150
  "access": "public",
152
151
  "provenance": true
153
152
  },
154
- "packageManager": "pnpm@10.15.0+sha512.486ebc259d3e999a4e8691ce03b5cac4a71cbeca39372a9b762cb500cfdf0873e2cb16abe3d951b1ee2cf012503f027b98b6584e4df22524e0c7450d9ec7aa7b",
155
- "pnpm": {
156
- "overrides": {
157
- "@conventional-changelog/git-client": ">=2.0.0",
158
- "glob": "^13.0.0"
159
- }
160
- }
153
+ "packageManager": "pnpm@10.34.5+sha512.a4ee05f2f73658255bd6a89859c065a45c28a57daefae2c893a168ee2b73168c37b91e83e57ea67654ad03f03031746430e8bce38e362e042605fb8abc80192e"
161
154
  }