@@ -82,20 +82,20 @@ export interface EncryptedData {
8282 */
8383export function isSecretExpression ( expression : string , secretInputs : string [ ] ) : boolean {
8484 const trimmed = expression . trim ( ) ;
85-
85+
8686 // All environment variables are treated as secrets
8787 if ( trimmed . startsWith ( "env." ) ) {
8888 return true ;
8989 }
90-
90+
9191 // Check if it's a secret input
9292 if ( trimmed . startsWith ( "inputs." ) ) {
9393 const inputPath = trimmed . slice ( "inputs." . length ) ;
9494 // Get the root input name (before any dots for nested access)
9595 const inputName = inputPath . split ( "." ) [ 0 ] ;
9696 return secretInputs . includes ( inputName ) ;
9797 }
98-
98+
9999 return false ;
100100}
101101
@@ -109,14 +109,14 @@ export function isSecretExpression(expression: string, secretInputs: string[]):
109109export function extractSecretExpressions ( template : string , secretInputs : string [ ] ) : string [ ] {
110110 const pattern = / \{ \{ ( [ ^ } ] + ) \} \} / g;
111111 const secrets : string [ ] = [ ] ;
112-
112+
113113 for ( const match of template . matchAll ( pattern ) ) {
114114 const expression = match [ 1 ] . trim ( ) ;
115115 if ( isSecretExpression ( expression , secretInputs ) ) {
116116 secrets . push ( expression ) ;
117117 }
118118 }
119-
119+
120120 return secrets ;
121121}
122122
@@ -148,12 +148,12 @@ export function maskSecretValue(value: string): string {
148148 if ( ! value || value . length === 0 ) {
149149 return SECRET_MASK ;
150150 }
151-
151+
152152 // For short values, completely mask
153153 if ( value . length <= 4 ) {
154154 return SECRET_MASK ;
155155 }
156-
156+
157157 // For longer values, show first character + mask
158158 return value [ 0 ] + SECRET_MASK ;
159159}
@@ -172,18 +172,18 @@ export function maskInterpolatedString(
172172 secretValues : Map < string , string >
173173) : string {
174174 let masked = interpolated ;
175-
175+
176176 // Sort by value length (longest first) to handle overlapping values correctly
177177 const sortedSecrets = Array . from ( secretValues . entries ( ) )
178178 . sort ( ( [ , a ] , [ , b ] ) => b . length - a . length ) ;
179-
179+
180180 for ( const [ , value ] of sortedSecrets ) {
181181 if ( value && value . length > 0 ) {
182182 // Replace all occurrences of the secret value with the mask
183183 masked = masked . split ( value ) . join ( SECRET_MASK ) ;
184184 }
185185 }
186-
186+
187187 return masked ;
188188}
189189
@@ -201,7 +201,7 @@ function validateEncryptionKey(encryptionKey: string): void {
201201 if ( ! encryptionKey || encryptionKey . length < MIN_KEY_LENGTH ) {
202202 throw new Error (
203203 `Encryption key must be at least ${ MIN_KEY_LENGTH } characters long for security. ` +
204- `Provided key is ${ encryptionKey ?. length || 0 } characters .`
204+ `Provided key does not meet security requirements .`
205205 ) ;
206206 }
207207}
@@ -229,22 +229,22 @@ function deriveKey(password: string, salt: Buffer): Buffer {
229229 */
230230export function encryptForStorage ( data : string , encryptionKey : string ) : EncryptedData {
231231 validateEncryptionKey ( encryptionKey ) ;
232-
232+
233233 const salt = randomBytes ( SALT_LENGTH ) ;
234234 const key = deriveKey ( encryptionKey , salt ) ;
235235 const iv = randomBytes ( IV_LENGTH ) ;
236-
236+
237237 const cipher = createCipheriv ( ENCRYPTION_ALGORITHM , key , iv ) ;
238238 const encrypted = Buffer . concat ( [
239239 cipher . update ( data , "utf8" ) ,
240240 cipher . final ( ) ,
241241 ] ) ;
242-
242+
243243 const authTag = cipher . getAuthTag ( ) ;
244-
244+
245245 // Combine salt + iv + authTag + encrypted data
246246 const combined = Buffer . concat ( [ salt , iv , authTag , encrypted ] ) ;
247-
247+
248248 return {
249249 encrypted : true ,
250250 data : combined . toString ( "base64" ) ,
@@ -261,25 +261,25 @@ export function encryptForStorage(data: string, encryptionKey: string): Encrypte
261261 */
262262export function decryptFromStorage ( encryptedData : EncryptedData , encryptionKey : string ) : string {
263263 validateEncryptionKey ( encryptionKey ) ;
264-
264+
265265 const combined = Buffer . from ( encryptedData . data , "base64" ) ;
266-
266+
267267 // Extract components
268268 const salt = combined . subarray ( 0 , SALT_LENGTH ) ;
269269 const iv = combined . subarray ( SALT_LENGTH , SALT_LENGTH + IV_LENGTH ) ;
270270 const authTag = combined . subarray ( SALT_LENGTH + IV_LENGTH , SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH ) ;
271271 const encrypted = combined . subarray ( SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH ) ;
272-
272+
273273 const key = deriveKey ( encryptionKey , salt ) ;
274-
274+
275275 const decipher = createDecipheriv ( ENCRYPTION_ALGORITHM , key , iv ) ;
276276 decipher . setAuthTag ( authTag ) ;
277-
277+
278278 const decrypted = Buffer . concat ( [
279279 decipher . update ( encrypted ) ,
280280 decipher . final ( ) ,
281281 ] ) ;
282-
282+
283283 return decrypted . toString ( "utf8" ) ;
284284}
285285
@@ -315,7 +315,7 @@ export function encryptSecretInputs(
315315 encryptionKey : string
316316) : Record < string , unknown > {
317317 const result : Record < string , unknown > = { } ;
318-
318+
319319 for ( const [ key , value ] of Object . entries ( inputs ) ) {
320320 if ( secretKeys . includes ( key ) && value !== undefined && value !== null ) {
321321 // Encrypt the secret value
@@ -325,7 +325,7 @@ export function encryptSecretInputs(
325325 result [ key ] = value ;
326326 }
327327 }
328-
328+
329329 return result ;
330330}
331331
@@ -341,7 +341,7 @@ export function decryptSecretInputs(
341341 encryptionKey : string
342342) : Record < string , unknown > {
343343 const result : Record < string , unknown > = { } ;
344-
344+
345345 for ( const [ key , value ] of Object . entries ( inputs ) ) {
346346 if ( isEncryptedData ( value ) ) {
347347 try {
@@ -352,14 +352,16 @@ export function decryptSecretInputs(
352352 } catch {
353353 result [ key ] = decrypted ;
354354 }
355- } catch {
356- // If decryption fails, keep the encrypted value
355+ } catch ( error ) {
356+ // Log warning for decryption failure - helps debug key rotation issues
357+ console . warn ( `[secrets] Failed to decrypt input '${ key } ': ${ error instanceof Error ? error . message : 'Unknown error' } ` ) ;
358+ // Keep the encrypted value to avoid data loss
357359 result [ key ] = value ;
358360 }
359361 } else {
360362 result [ key ] = value ;
361363 }
362364 }
363-
365+
364366 return result ;
365367}
0 commit comments