diff --git a/packages/cli/src/credentials/__tests__/external-secrets.utils.test.ts b/packages/cli/src/credentials/__tests__/external-secrets.utils.test.ts index 17572978c2d92..49b15c67c39be 100644 --- a/packages/cli/src/credentials/__tests__/external-secrets.utils.test.ts +++ b/packages/cli/src/credentials/__tests__/external-secrets.utils.test.ts @@ -63,6 +63,37 @@ describe('External secrets utils', () => { const result = extractProviderKeysFromExpression(expression); expect(result.sort()).toEqual(['aws', 'vault']); }); + + it('extracts provider from mixed dot and double-quoted bracket notation', () => { + expect( + extractProviderKeysFromExpression('={{ $secrets.azureKeyVault["postgres-n8n-data"] }}'), + ).toEqual(['azureKeyVault']); + }); + + it('extracts provider from mixed dot and single-quoted bracket notation', () => { + expect( + extractProviderKeysFromExpression("={{ $secrets.azureKeyVault['postgres-n8n-data'] }}"), + ).toEqual(['azureKeyVault']); + }); + + it('extracts hyphenated provider from mixed dot and bracket notation', () => { + expect( + extractProviderKeysFromExpression('={{ $secrets.my-vault["postgres-n8n-data"] }}'), + ).toEqual(['my-vault']); + }); + + it('extracts provider when bracket access is followed by further dot access', () => { + expect(extractProviderKeysFromExpression('={{ $secrets.vault["key"].subfield }}')).toEqual([ + 'vault', + ]); + }); + + it('extracts multiple providers from expression mixing dot and bracket notation', () => { + const result = extractProviderKeysFromExpression( + '={{ $secrets.vault["a"] + ":" + $secrets.aws.b }}', + ); + expect(result.sort()).toEqual(['aws', 'vault']); + }); }); describe('getExternalSecretExpressionPaths', () => { diff --git a/packages/cli/src/credentials/external-secrets.utils.ts b/packages/cli/src/credentials/external-secrets.utils.ts index 326e650613754..3760568b90894 100644 --- a/packages/cli/src/credentials/external-secrets.utils.ts +++ b/packages/cli/src/credentials/external-secrets.utils.ts @@ -38,9 +38,12 @@ export function extractProviderKeysFromExpression(expression: string): string[] for (const expression of expressionBlocks) { const expressionContent = expression[1]; // Content inside {{ }} - // Match all dot notation occurrences: $secrets.providerKey + // Match dot-accessed provider keys: $secrets.providerKey. The lookahead + // accepts either a further dot ($secrets.vault.key) or an opening + // bracket ($secrets.vault["key"]) so mixed expressions still resolve + // the provider key when the secret name can't use dot notation. const dotMatches = expressionContent.matchAll( - new RegExp(`\\$secrets\\.(${PROVIDER_KEY_PATTERN})(?=\\.)`, 'g'), + new RegExp(`\\$secrets\\.(${PROVIDER_KEY_PATTERN})(?=[.\\[])`, 'g'), ); for (const match of dotMatches) { providerKeys.add(match[1]);