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
package/dist/lib/security.d.ts
CHANGED
|
@@ -1,21 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Security utilities for input validation and sanitization
|
|
3
|
+
* Focused on CLI tool security concerns: path traversal, injection prevention
|
|
4
|
+
* @since 2.8.0+
|
|
5
|
+
*/
|
|
6
|
+
/** Options controlling path validation behaviour. */
|
|
1
7
|
export interface PathValidationOptions {
|
|
8
|
+
/**
|
|
9
|
+
* When true, accepts absolute destinations even if they fall outside the allowed base.
|
|
10
|
+
* @since 2.12.2 Enables secure chart writes to system-level directories.
|
|
11
|
+
*/
|
|
2
12
|
allowAbsolute?: boolean;
|
|
3
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Security utilities for input validation and sanitization
|
|
16
|
+
* Focused on CLI tool security concerns: path traversal, injection prevention
|
|
17
|
+
*
|
|
18
|
+
* @since 2.8.0+
|
|
19
|
+
*/
|
|
4
20
|
export declare class SecurityUtils {
|
|
21
|
+
/**
|
|
22
|
+
* Validates and sanitizes file paths to prevent path traversal attacks
|
|
23
|
+
* @param inputPath - The path to validate
|
|
24
|
+
* @param allowedBasePath - The base path that the input should be within
|
|
25
|
+
* @returns Sanitized path if valid
|
|
26
|
+
* @throws Error if path is invalid or contains traversal sequences
|
|
27
|
+
*
|
|
28
|
+
* @since 2.8.0+
|
|
29
|
+
*/
|
|
5
30
|
static validatePath(inputPath: string, allowedBasePath: string, options?: PathValidationOptions): string;
|
|
31
|
+
/**
|
|
32
|
+
* Sanitizes log messages to prevent log injection attacks
|
|
33
|
+
* @param message - The message to sanitize
|
|
34
|
+
* @returns Sanitized message with control characters removed
|
|
35
|
+
*
|
|
36
|
+
* @since 2.8.0+
|
|
37
|
+
*/
|
|
6
38
|
static sanitizeLogMessage(message: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Sanitizes environment names to prevent path traversal (CWE-22)
|
|
41
|
+
* @param env - Environment name to sanitize
|
|
42
|
+
* @returns Sanitized environment name
|
|
43
|
+
* @throws Error if environment name is invalid
|
|
44
|
+
*
|
|
45
|
+
* @since 2.8.0+
|
|
46
|
+
*/
|
|
7
47
|
static sanitizeEnvironmentName(env: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* Validates TypeScript file extensions for dynamic imports
|
|
50
|
+
* @param filePath - The file path to validate
|
|
51
|
+
* @returns True if the file has a valid TypeScript extension
|
|
52
|
+
*
|
|
53
|
+
* @since 2.8.0+
|
|
54
|
+
*/
|
|
8
55
|
static isValidTypeScriptFile(filePath: string): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Validates if a path is safe for use in Helm templates
|
|
58
|
+
* Enhanced validation with better security checks and Helm best practices
|
|
59
|
+
* @param templatePath - The template path to validate
|
|
60
|
+
* @returns True if the path is valid for Helm templates
|
|
61
|
+
*
|
|
62
|
+
* @since 2.8.0+
|
|
63
|
+
*/
|
|
9
64
|
static isValidHelmTemplatePath(templatePath: string): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Validates chart names according to Helm conventions
|
|
67
|
+
* @param chartName - Chart name to validate
|
|
68
|
+
* @returns True if chart name follows Helm naming rules
|
|
69
|
+
*
|
|
70
|
+
* @since 2.8.0+
|
|
71
|
+
*/
|
|
10
72
|
static isValidChartName(chartName: string): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Validates subchart names according to Helm conventions
|
|
75
|
+
* @param subchartName - Subchart name to validate
|
|
76
|
+
* @returns True if subchart name is valid
|
|
77
|
+
*
|
|
78
|
+
* @since 2.8.0+
|
|
79
|
+
*/
|
|
11
80
|
static isValidSubchartName(subchartName: string): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Sanitizes environment variable names and values for Kubernetes security
|
|
83
|
+
* Prevents injection attacks and ensures compliance with Kubernetes naming rules
|
|
84
|
+
* @param name - Environment variable name to sanitize
|
|
85
|
+
* @param value - Environment variable value to sanitize
|
|
86
|
+
* @returns Object with sanitized name and value
|
|
87
|
+
* @throws Error if name or value contains dangerous patterns
|
|
88
|
+
*
|
|
89
|
+
* @since 2.8.0+
|
|
90
|
+
*/
|
|
12
91
|
static sanitizeEnvVar(name: string, value: string): {
|
|
13
92
|
name: string;
|
|
14
93
|
value: string;
|
|
15
94
|
};
|
|
95
|
+
/**
|
|
96
|
+
* Validates container image tags for security best practices
|
|
97
|
+
* Prevents use of dangerous tags and ensures proper versioning
|
|
98
|
+
* @param tag - Image tag to validate
|
|
99
|
+
* @returns True if tag is valid and secure
|
|
100
|
+
* @throws Error if tag violates security policies
|
|
101
|
+
*
|
|
102
|
+
* @since 2.8.0+
|
|
103
|
+
*/
|
|
16
104
|
static validateImageTag(tag: string): boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Generates secure names for Kubernetes secrets
|
|
107
|
+
* Ensures names follow RFC 1123 and are safe for Kubernetes
|
|
108
|
+
* @param baseName - Base name for the secret
|
|
109
|
+
* @param suffix - Optional suffix to append
|
|
110
|
+
* @returns Secure secret name
|
|
111
|
+
* @throws Error if generated name is invalid
|
|
112
|
+
*
|
|
113
|
+
* @since 2.8.0+
|
|
114
|
+
*/
|
|
17
115
|
static generateSecretName(baseName: string, suffix?: string): string;
|
|
116
|
+
/**
|
|
117
|
+
* Sanitizes the base name for secret generation using safe string methods
|
|
118
|
+
* @param baseName - Base name to sanitize
|
|
119
|
+
* @returns Sanitized base name
|
|
120
|
+
* @throws Error if no valid characters remain
|
|
121
|
+
*
|
|
122
|
+
* @since 2.8.0+
|
|
123
|
+
*/
|
|
18
124
|
private static sanitizeBaseName;
|
|
125
|
+
/**
|
|
126
|
+
* Appends suffix to sanitized base name using safe string methods
|
|
127
|
+
* @param baseName - Sanitized base name
|
|
128
|
+
* @param suffix - Suffix to append
|
|
129
|
+
* @returns Base name with suffix
|
|
130
|
+
* @throws Error if suffix is invalid
|
|
131
|
+
*
|
|
132
|
+
* @since 2.8.0+
|
|
133
|
+
*/
|
|
19
134
|
private static appendSuffix;
|
|
135
|
+
/**
|
|
136
|
+
* Truncates name if it exceeds length limits
|
|
137
|
+
* @param name - Name to potentially truncate
|
|
138
|
+
* @param suffix - Original suffix for preservation
|
|
139
|
+
* @returns Truncated name if needed
|
|
140
|
+
*
|
|
141
|
+
* @since 2.8.0+
|
|
142
|
+
*/
|
|
20
143
|
private static truncateIfNeeded;
|
|
21
144
|
}
|
package/dist/lib/security.js
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Security utilities for input validation and sanitization
|
|
3
|
+
* Focused on CLI tool security concerns: path traversal, injection prevention
|
|
4
|
+
* @since 2.8.0+
|
|
5
|
+
*/
|
|
1
6
|
import * as path from 'path';
|
|
7
|
+
/**
|
|
8
|
+
* Security utilities for input validation and sanitization
|
|
9
|
+
* Focused on CLI tool security concerns: path traversal, injection prevention
|
|
10
|
+
*
|
|
11
|
+
* @since 2.8.0+
|
|
12
|
+
*/
|
|
2
13
|
export class SecurityUtils {
|
|
14
|
+
/**
|
|
15
|
+
* Validates and sanitizes file paths to prevent path traversal attacks
|
|
16
|
+
* @param inputPath - The path to validate
|
|
17
|
+
* @param allowedBasePath - The base path that the input should be within
|
|
18
|
+
* @returns Sanitized path if valid
|
|
19
|
+
* @throws Error if path is invalid or contains traversal sequences
|
|
20
|
+
*
|
|
21
|
+
* @since 2.8.0+
|
|
22
|
+
*/
|
|
3
23
|
static validatePath(inputPath, allowedBasePath, options) {
|
|
4
24
|
if (!inputPath || typeof inputPath !== 'string') {
|
|
5
25
|
throw new Error('Invalid path: path must be a non-empty string');
|
|
@@ -24,6 +44,7 @@ export class SecurityUtils {
|
|
|
24
44
|
registerCandidate(normalized);
|
|
25
45
|
}
|
|
26
46
|
catch {
|
|
47
|
+
// Ignore normalization errors and continue with existing candidates.
|
|
27
48
|
}
|
|
28
49
|
};
|
|
29
50
|
addCandidate(inputPath);
|
|
@@ -58,6 +79,7 @@ export class SecurityUtils {
|
|
|
58
79
|
throw new Error('Invalid path: path traversal sequences detected');
|
|
59
80
|
}
|
|
60
81
|
}
|
|
82
|
+
// Resolve and normalize paths
|
|
61
83
|
const resolvedInput = path.resolve(inputPath);
|
|
62
84
|
const resolvedBase = path.resolve(allowedBasePath);
|
|
63
85
|
const relativePath = path.relative(resolvedBase, resolvedInput);
|
|
@@ -67,21 +89,39 @@ export class SecurityUtils {
|
|
|
67
89
|
}
|
|
68
90
|
return resolvedInput;
|
|
69
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Sanitizes log messages to prevent log injection attacks
|
|
94
|
+
* @param message - The message to sanitize
|
|
95
|
+
* @returns Sanitized message with control characters removed
|
|
96
|
+
*
|
|
97
|
+
* @since 2.8.0+
|
|
98
|
+
*/
|
|
70
99
|
static sanitizeLogMessage(message) {
|
|
71
100
|
if (typeof message !== 'string') {
|
|
72
101
|
return String(message);
|
|
73
102
|
}
|
|
103
|
+
// Remove control characters (ASCII 0-31 and 127) and normalize line endings
|
|
74
104
|
return (message
|
|
75
|
-
.replace(/\r\n/g, '\n')
|
|
76
|
-
|
|
77
|
-
.replace(/[\
|
|
78
|
-
.replace(
|
|
105
|
+
.replace(/\r\n/g, '\n') // Normalize CRLF to LF
|
|
106
|
+
// eslint-disable-next-line no-control-regex -- Intentionally removing control characters for security
|
|
107
|
+
.replace(/[\x00-\x09\x0B-\x0C\x0E-\x1F\x7F]/g, '') // Strip control chars except LF/CR
|
|
108
|
+
.replace(/[\r\n]/g, ' ') // Normalize remaining line breaks
|
|
109
|
+
.replace(/\s+/g, ' ') // Collapse repeated whitespace
|
|
79
110
|
.trim());
|
|
80
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Sanitizes environment names to prevent path traversal (CWE-22)
|
|
114
|
+
* @param env - Environment name to sanitize
|
|
115
|
+
* @returns Sanitized environment name
|
|
116
|
+
* @throws Error if environment name is invalid
|
|
117
|
+
*
|
|
118
|
+
* @since 2.8.0+
|
|
119
|
+
*/
|
|
81
120
|
static sanitizeEnvironmentName(env) {
|
|
82
121
|
if (!env || typeof env !== 'string') {
|
|
83
122
|
throw new Error('Environment name must be a non-empty string');
|
|
84
123
|
}
|
|
124
|
+
// Allow only alphanumeric, hyphens, and underscores
|
|
85
125
|
const sanitized = env.replace(/[^a-zA-Z0-9-_]/g, '');
|
|
86
126
|
if (sanitized !== env) {
|
|
87
127
|
throw new Error(`Invalid environment name: ${SecurityUtils.sanitizeLogMessage(env)}`);
|
|
@@ -91,52 +131,105 @@ export class SecurityUtils {
|
|
|
91
131
|
}
|
|
92
132
|
return sanitized;
|
|
93
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Validates TypeScript file extensions for dynamic imports
|
|
136
|
+
* @param filePath - The file path to validate
|
|
137
|
+
* @returns True if the file has a valid TypeScript extension
|
|
138
|
+
*
|
|
139
|
+
* @since 2.8.0+
|
|
140
|
+
*/
|
|
94
141
|
static isValidTypeScriptFile(filePath) {
|
|
95
142
|
const allowedExtensions = ['.ts', '.tsx', '.cts', '.mts'];
|
|
96
143
|
const ext = path.extname(filePath);
|
|
97
144
|
return allowedExtensions.includes(ext);
|
|
98
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Validates if a path is safe for use in Helm templates
|
|
148
|
+
* Enhanced validation with better security checks and Helm best practices
|
|
149
|
+
* @param templatePath - The template path to validate
|
|
150
|
+
* @returns True if the path is valid for Helm templates
|
|
151
|
+
*
|
|
152
|
+
* @since 2.8.0+
|
|
153
|
+
*/
|
|
99
154
|
static isValidHelmTemplatePath(templatePath) {
|
|
100
155
|
if (!templatePath || typeof templatePath !== 'string') {
|
|
101
156
|
return false;
|
|
102
157
|
}
|
|
158
|
+
// Check length limits (reasonable for Helm paths)
|
|
103
159
|
if (templatePath.length > 253) {
|
|
104
160
|
return false;
|
|
105
161
|
}
|
|
162
|
+
// Enhanced validation following Kubernetes naming conventions
|
|
163
|
+
// Check if starts and ends with alphanumeric
|
|
106
164
|
if (!/^[a-zA-Z0-9]/.test(templatePath) || !/[a-zA-Z0-9]$/.test(templatePath)) {
|
|
107
165
|
return false;
|
|
108
166
|
}
|
|
167
|
+
// Check for valid characters only (safe regex)
|
|
109
168
|
if (!/^[a-zA-Z0-9._\-/]+$/.test(templatePath)) {
|
|
110
169
|
return false;
|
|
111
170
|
}
|
|
171
|
+
// Additional security checks
|
|
172
|
+
// Prevent path traversal attempts
|
|
112
173
|
if (templatePath.includes('..') || templatePath.includes('//')) {
|
|
113
174
|
return false;
|
|
114
175
|
}
|
|
176
|
+
// Prevent reserved words that could cause issues
|
|
115
177
|
const reservedWords = ['nil', 'null', 'undefined', 'true', 'false'];
|
|
178
|
+
// Split by directory separators first
|
|
116
179
|
const pathParts = templatePath.split('/');
|
|
117
180
|
for (const part of pathParts) {
|
|
181
|
+
// Then split by dots for file extensions/parts
|
|
118
182
|
const segments = part.split('.');
|
|
119
183
|
for (const segment of segments) {
|
|
120
184
|
if (reservedWords.includes(segment.toLowerCase())) {
|
|
121
185
|
return false;
|
|
122
186
|
}
|
|
187
|
+
// Each segment should not be empty (except possibly for leading dot files, but split handles that)
|
|
123
188
|
if (segment.length === 0 && segments.length > 1) {
|
|
189
|
+
// Allow .gitignore (empty first segment)
|
|
124
190
|
continue;
|
|
125
191
|
}
|
|
126
192
|
}
|
|
127
193
|
}
|
|
128
194
|
return true;
|
|
129
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Validates chart names according to Helm conventions
|
|
198
|
+
* @param chartName - Chart name to validate
|
|
199
|
+
* @returns True if chart name follows Helm naming rules
|
|
200
|
+
*
|
|
201
|
+
* @since 2.8.0+
|
|
202
|
+
*/
|
|
130
203
|
static isValidChartName(chartName) {
|
|
131
204
|
if (!chartName || typeof chartName !== 'string') {
|
|
132
205
|
return false;
|
|
133
206
|
}
|
|
207
|
+
// Helm chart name validation (RFC 1123 subdomain) - safe regex
|
|
208
|
+
// Must start with a letter, can contain letters, numbers, and hyphens
|
|
209
|
+
// eslint-disable-next-line security/detect-unsafe-regex -- Simple character class regex is safe
|
|
134
210
|
const chartNameRegex = /^[a-z]([-a-z0-9]*[a-z0-9])?$/;
|
|
135
211
|
return chartNameRegex.test(chartName) && chartName.length <= 63;
|
|
136
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Validates subchart names according to Helm conventions
|
|
215
|
+
* @param subchartName - Subchart name to validate
|
|
216
|
+
* @returns True if subchart name is valid
|
|
217
|
+
*
|
|
218
|
+
* @since 2.8.0+
|
|
219
|
+
*/
|
|
137
220
|
static isValidSubchartName(subchartName) {
|
|
138
221
|
return this.isValidChartName(subchartName);
|
|
139
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Sanitizes environment variable names and values for Kubernetes security
|
|
225
|
+
* Prevents injection attacks and ensures compliance with Kubernetes naming rules
|
|
226
|
+
* @param name - Environment variable name to sanitize
|
|
227
|
+
* @param value - Environment variable value to sanitize
|
|
228
|
+
* @returns Object with sanitized name and value
|
|
229
|
+
* @throws Error if name or value contains dangerous patterns
|
|
230
|
+
*
|
|
231
|
+
* @since 2.8.0+
|
|
232
|
+
*/
|
|
140
233
|
static sanitizeEnvVar(name, value) {
|
|
141
234
|
if (!name || typeof name !== 'string') {
|
|
142
235
|
throw new Error('Environment variable name must be a non-empty string');
|
|
@@ -144,7 +237,9 @@ export class SecurityUtils {
|
|
|
144
237
|
if (typeof value !== 'string') {
|
|
145
238
|
throw new Error('Environment variable value must be a string');
|
|
146
239
|
}
|
|
240
|
+
// Kubernetes env var name validation (RFC 1123 compatible)
|
|
147
241
|
let sanitizedName = name.toUpperCase().replace(/[^A-Z0-9_]/g, '_');
|
|
242
|
+
// Ensure name starts with letter or underscore
|
|
148
243
|
if (/^[0-9]/.test(sanitizedName)) {
|
|
149
244
|
sanitizedName = `_${sanitizedName}`;
|
|
150
245
|
}
|
|
@@ -152,6 +247,7 @@ export class SecurityUtils {
|
|
|
152
247
|
if (!nameRegex.test(sanitizedName)) {
|
|
153
248
|
throw new Error(`Invalid environment variable name: ${SecurityUtils.sanitizeLogMessage(name)}`);
|
|
154
249
|
}
|
|
250
|
+
// Check for dangerous patterns in value using safer string methods
|
|
155
251
|
const hasDangerousPattern = (val) => {
|
|
156
252
|
if (val.includes('$(') && val.includes(')'))
|
|
157
253
|
return 'command substitution';
|
|
@@ -159,6 +255,7 @@ export class SecurityUtils {
|
|
|
159
255
|
return 'backtick execution';
|
|
160
256
|
if (val.includes('${') && val.includes('}'))
|
|
161
257
|
return 'variable expansion';
|
|
258
|
+
// Check for control characters
|
|
162
259
|
for (let i = 0; i < val.length; i++) {
|
|
163
260
|
const code = val.charCodeAt(i);
|
|
164
261
|
if ((code >= 0x00 && code <= 0x08) ||
|
|
@@ -175,6 +272,7 @@ export class SecurityUtils {
|
|
|
175
272
|
if (dangerousPattern) {
|
|
176
273
|
throw new Error(`Environment variable value contains dangerous pattern (${dangerousPattern}): ${SecurityUtils.sanitizeLogMessage(value)}`);
|
|
177
274
|
}
|
|
275
|
+
// Limit value length to prevent DoS
|
|
178
276
|
if (value.length > 32768) {
|
|
179
277
|
throw new Error('Environment variable value exceeds maximum length (32KB)');
|
|
180
278
|
}
|
|
@@ -183,31 +281,58 @@ export class SecurityUtils {
|
|
|
183
281
|
value: value.trim(),
|
|
184
282
|
};
|
|
185
283
|
}
|
|
284
|
+
/**
|
|
285
|
+
* Validates container image tags for security best practices
|
|
286
|
+
* Prevents use of dangerous tags and ensures proper versioning
|
|
287
|
+
* @param tag - Image tag to validate
|
|
288
|
+
* @returns True if tag is valid and secure
|
|
289
|
+
* @throws Error if tag violates security policies
|
|
290
|
+
*
|
|
291
|
+
* @since 2.8.0+
|
|
292
|
+
*/
|
|
186
293
|
static validateImageTag(tag) {
|
|
187
294
|
if (!tag || typeof tag !== 'string') {
|
|
188
295
|
throw new Error('Image tag must be a non-empty string');
|
|
189
296
|
}
|
|
190
297
|
const trimmedTag = tag.trim();
|
|
298
|
+
// Reject dangerous or non-specific tags
|
|
191
299
|
const dangerousTags = ['latest', 'master', 'main', 'dev', 'development', 'test', 'staging'];
|
|
192
300
|
if (dangerousTags.includes(trimmedTag.toLowerCase())) {
|
|
193
301
|
throw new Error(`Insecure image tag detected: '${trimmedTag}'. Use specific version tags for security.`);
|
|
194
302
|
}
|
|
303
|
+
// Validate tag format (Docker tag rules)
|
|
195
304
|
const tagRegex = /^[a-zA-Z0-9._-]+$/;
|
|
196
305
|
if (!tagRegex.test(trimmedTag)) {
|
|
197
306
|
throw new Error(`Invalid image tag format: ${SecurityUtils.sanitizeLogMessage(trimmedTag)}`);
|
|
198
307
|
}
|
|
308
|
+
// Check length limits
|
|
199
309
|
if (trimmedTag.length > 128) {
|
|
200
310
|
throw new Error('Image tag exceeds maximum length (128 characters)');
|
|
201
311
|
}
|
|
312
|
+
// Prefer semantic versioning patterns
|
|
313
|
+
// eslint-disable-next-line security/detect-unsafe-regex -- Simple regex patterns are safe for validation
|
|
202
314
|
const semverPattern = /^v?\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?$/;
|
|
203
315
|
const hashPattern = /^[a-f0-9]{7,64}$/;
|
|
316
|
+
// eslint-disable-next-line security/detect-unsafe-regex -- Simple date pattern is safe for validation
|
|
204
317
|
const datePattern = /^\d{4}-\d{2}-\d{2}(-[a-zA-Z0-9.-]+)?$/;
|
|
205
318
|
if (!semverPattern.test(trimmedTag) &&
|
|
206
319
|
!hashPattern.test(trimmedTag) &&
|
|
207
320
|
!datePattern.test(trimmedTag)) {
|
|
321
|
+
// Warning for non-standard tags but don't fail
|
|
322
|
+
// Note: Non-standard tag pattern detected but allowing it
|
|
208
323
|
}
|
|
209
324
|
return true;
|
|
210
325
|
}
|
|
326
|
+
/**
|
|
327
|
+
* Generates secure names for Kubernetes secrets
|
|
328
|
+
* Ensures names follow RFC 1123 and are safe for Kubernetes
|
|
329
|
+
* @param baseName - Base name for the secret
|
|
330
|
+
* @param suffix - Optional suffix to append
|
|
331
|
+
* @returns Secure secret name
|
|
332
|
+
* @throws Error if generated name is invalid
|
|
333
|
+
*
|
|
334
|
+
* @since 2.8.0+
|
|
335
|
+
*/
|
|
211
336
|
static generateSecretName(baseName, suffix) {
|
|
212
337
|
if (!baseName || typeof baseName !== 'string') {
|
|
213
338
|
throw new Error('Base name must be a non-empty string');
|
|
@@ -216,16 +341,27 @@ export class SecurityUtils {
|
|
|
216
341
|
if (suffix) {
|
|
217
342
|
sanitizedBase = this.appendSuffix(sanitizedBase, suffix);
|
|
218
343
|
}
|
|
344
|
+
// Ensure name starts with letter (not number)
|
|
219
345
|
if (/^[0-9]/.test(sanitizedBase)) {
|
|
220
346
|
sanitizedBase = `s${sanitizedBase}`;
|
|
221
347
|
}
|
|
222
348
|
sanitizedBase = this.truncateIfNeeded(sanitizedBase, suffix);
|
|
349
|
+
// Final validation
|
|
223
350
|
if (!this.isValidChartName(sanitizedBase)) {
|
|
224
351
|
throw new Error(`Generated secret name is invalid: ${sanitizedBase}`);
|
|
225
352
|
}
|
|
226
353
|
return sanitizedBase;
|
|
227
354
|
}
|
|
355
|
+
/**
|
|
356
|
+
* Sanitizes the base name for secret generation using safe string methods
|
|
357
|
+
* @param baseName - Base name to sanitize
|
|
358
|
+
* @returns Sanitized base name
|
|
359
|
+
* @throws Error if no valid characters remain
|
|
360
|
+
*
|
|
361
|
+
* @since 2.8.0+
|
|
362
|
+
*/
|
|
228
363
|
static sanitizeBaseName(baseName) {
|
|
364
|
+
// Use character-by-character processing instead of regex
|
|
229
365
|
let sanitized = '';
|
|
230
366
|
for (const char of baseName.toLowerCase()) {
|
|
231
367
|
if ((char >= 'a' && char <= 'z') || (char >= '0' && char <= '9') || char === '-') {
|
|
@@ -235,6 +371,7 @@ export class SecurityUtils {
|
|
|
235
371
|
sanitized += '-';
|
|
236
372
|
}
|
|
237
373
|
}
|
|
374
|
+
// Collapse multiple hyphens using split/filter/join
|
|
238
375
|
const parts = sanitized.split('-').filter((part) => part.length > 0);
|
|
239
376
|
sanitized = parts.join('-');
|
|
240
377
|
if (!sanitized) {
|
|
@@ -242,10 +379,20 @@ export class SecurityUtils {
|
|
|
242
379
|
}
|
|
243
380
|
return sanitized;
|
|
244
381
|
}
|
|
382
|
+
/**
|
|
383
|
+
* Appends suffix to sanitized base name using safe string methods
|
|
384
|
+
* @param baseName - Sanitized base name
|
|
385
|
+
* @param suffix - Suffix to append
|
|
386
|
+
* @returns Base name with suffix
|
|
387
|
+
* @throws Error if suffix is invalid
|
|
388
|
+
*
|
|
389
|
+
* @since 2.8.0+
|
|
390
|
+
*/
|
|
245
391
|
static appendSuffix(baseName, suffix) {
|
|
246
392
|
if (typeof suffix !== 'string') {
|
|
247
393
|
throw new Error('Suffix must be a string');
|
|
248
394
|
}
|
|
395
|
+
// Use character-by-character processing instead of regex
|
|
249
396
|
let sanitizedSuffix = '';
|
|
250
397
|
for (const char of suffix.toLowerCase()) {
|
|
251
398
|
if ((char >= 'a' && char <= 'z') || (char >= '0' && char <= '9') || char === '-') {
|
|
@@ -255,6 +402,7 @@ export class SecurityUtils {
|
|
|
255
402
|
sanitizedSuffix += '-';
|
|
256
403
|
}
|
|
257
404
|
}
|
|
405
|
+
// Collapse multiple hyphens using split/filter/join
|
|
258
406
|
const parts = sanitizedSuffix.split('-').filter((part) => part.length > 0);
|
|
259
407
|
sanitizedSuffix = parts.join('-');
|
|
260
408
|
if (sanitizedSuffix) {
|
|
@@ -262,12 +410,22 @@ export class SecurityUtils {
|
|
|
262
410
|
}
|
|
263
411
|
return baseName;
|
|
264
412
|
}
|
|
413
|
+
/**
|
|
414
|
+
* Truncates name if it exceeds length limits
|
|
415
|
+
* @param name - Name to potentially truncate
|
|
416
|
+
* @param suffix - Original suffix for preservation
|
|
417
|
+
* @returns Truncated name if needed
|
|
418
|
+
*
|
|
419
|
+
* @since 2.8.0+
|
|
420
|
+
*/
|
|
265
421
|
static truncateIfNeeded(name, suffix) {
|
|
266
422
|
const maxLength = 63;
|
|
267
423
|
if (name.length <= maxLength) {
|
|
268
424
|
return name;
|
|
269
425
|
}
|
|
426
|
+
// Truncate but preserve suffix if possible
|
|
270
427
|
if (suffix) {
|
|
428
|
+
// Use safe character processing instead of regex
|
|
271
429
|
let safeSuffix = '';
|
|
272
430
|
for (const char of suffix.toLowerCase()) {
|
|
273
431
|
if ((char >= 'a' && char <= 'z') || (char >= '0' && char <= '9') || char === '-') {
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Flexible subchart template that supports any cdk8s/cdk8s-plus-33 construct
|
|
3
|
+
* @since 2.8.4
|
|
4
|
+
*/
|
|
1
5
|
import { Chart } from 'cdk8s';
|
|
6
|
+
/**
|
|
7
|
+
* Flexible subchart that can accept any cdk8s construct or function
|
|
8
|
+
* @since 2.8.4
|
|
9
|
+
*/
|
|
2
10
|
export declare class FlexibleSubchart extends Chart {
|
|
3
11
|
private readonly config;
|
|
4
12
|
private _constructs;
|
|
@@ -11,18 +19,62 @@ export declare class FlexibleSubchart extends Chart {
|
|
|
11
19
|
manifest?: unknown;
|
|
12
20
|
[key: string]: unknown;
|
|
13
21
|
});
|
|
22
|
+
/**
|
|
23
|
+
* Add any cdk8s construct
|
|
24
|
+
* @param construct - Any cdk8s construct
|
|
25
|
+
* @param id - Unique identifier
|
|
26
|
+
*/
|
|
14
27
|
addConstruct(construct: unknown, id?: string): void;
|
|
28
|
+
/**
|
|
29
|
+
* Add any manifest (for addManifest compatibility)
|
|
30
|
+
* @param manifest - Any manifest object
|
|
31
|
+
* @param id - Unique identifier
|
|
32
|
+
*/
|
|
15
33
|
addManifest(manifest: unknown, id?: string): void;
|
|
34
|
+
/**
|
|
35
|
+
* Configure the subchart with any provided construct or manifest
|
|
36
|
+
* @private
|
|
37
|
+
*/
|
|
16
38
|
private configure;
|
|
39
|
+
/**
|
|
40
|
+
* Write Helm chart files for this subchart
|
|
41
|
+
* @param outputDir - Directory to write the Helm chart files
|
|
42
|
+
*/
|
|
17
43
|
writeHelmChart(outputDir: string): void;
|
|
44
|
+
/**
|
|
45
|
+
* Generate Helm template for a manifest
|
|
46
|
+
* @param manifest - The manifest object
|
|
47
|
+
* @param id - Template identifier
|
|
48
|
+
* @returns Generated template content
|
|
49
|
+
* @private
|
|
50
|
+
*/
|
|
18
51
|
private _generateManifestTemplate;
|
|
52
|
+
/**
|
|
53
|
+
* Get all constructs added to this subchart
|
|
54
|
+
*/
|
|
19
55
|
getConstructs(): unknown[];
|
|
56
|
+
/**
|
|
57
|
+
* Get all manifests added to this subchart
|
|
58
|
+
*/
|
|
20
59
|
getManifests(): unknown[];
|
|
21
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Create a flexible subchart that can accept any construct
|
|
63
|
+
* @param scope - Parent chart scope
|
|
64
|
+
* @param id - Unique identifier
|
|
65
|
+
* @param config - Subchart configuration
|
|
66
|
+
* @returns FlexibleSubchart instance
|
|
67
|
+
*/
|
|
22
68
|
export declare function createFlexibleSubchart(scope: Chart, id: string, config: {
|
|
23
69
|
name: string;
|
|
24
70
|
version?: string;
|
|
25
71
|
description?: string;
|
|
26
72
|
[key: string]: unknown;
|
|
27
73
|
}): FlexibleSubchart;
|
|
74
|
+
/**
|
|
75
|
+
* Generate a simple subchart template using Rutter addManifest()
|
|
76
|
+
* @param name Subchart name
|
|
77
|
+
* @returns Template string for chart.ts with simple Rutter implementation
|
|
78
|
+
* @since 2.11.0
|
|
79
|
+
*/
|
|
28
80
|
export declare function generateFlexibleSubchartTemplate(name: string): string;
|