From c3950e31cf9c3c0d2ff9f7c18d862f843a158903 Mon Sep 17 00:00:00 2001 From: Danny Ward Date: Mon, 26 Oct 2026 14:48:04 -0400 Subject: [PATCH] fix: ignore nonliteral backend IDs Fixes #331 --- src/services/transitive-resolver.ts | 12 ++++++- .../services/transitive-extractor.test.ts | 33 +++++++++++++++++++ .../unit/services/transitive-resolver.test.ts | 15 +++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/services/transitive-resolver.ts b/src/services/transitive-resolver.ts index 1251e823..2b28dde6 100644 --- a/src/services/transitive-resolver.ts +++ b/src/services/transitive-resolver.ts @@ -21,6 +21,16 @@ const NAMED_VALUE_PATTERN = /\{\{([^}]+)\}\}/g; const BACKEND_PATTERN = / 0 && + !backendId.includes('{{') && + !backendId.startsWith('@(') && + !backendId.startsWith('@{') + ); +} + /** * Represents a discovered transitive dependency. */ @@ -60,7 +70,7 @@ export function scanPolicyReferences(policyXml: string): TransitiveDependency[] // Backend references for (const match of policyXml.matchAll(BACKEND_PATTERN)) { - if (match[1]) { + if (match[1] && isLiteralBackendId(match[1])) { dependencies.push({ type: ResourceType.Backend, name: match[1].trim(), diff --git a/tests/unit/services/transitive-extractor.test.ts b/tests/unit/services/transitive-extractor.test.ts index 31ead3d4..c1c82ef0 100644 --- a/tests/unit/services/transitive-extractor.test.ts +++ b/tests/unit/services/transitive-extractor.test.ts @@ -244,4 +244,37 @@ describe('transitive-extractor', () => { expect(result.extractedDescriptors).toEqual([]); }); }); + + describe('backend named-value references (#331)', () => { + it('should extract the named value without requesting a backend for its placeholder', async () => { + const client = createMockClient(); + client.listResources = namedValueList({ name: 'demo-backend-id', displayName: 'demo-backend-id' }); + client.getResource.mockImplementation(async (_ctx, descriptor: ResourceDescriptor) => { + if (descriptor.type === ResourceType.NamedValue && descriptor.nameParts[0] === 'demo-backend-id') { + return { + name: 'demo-backend-id', + properties: { displayName: 'demo-backend-id', secret: false, value: 'backend-xyz' }, + }; + } + return undefined; + }); + const store = createMockStore(); + const policies = new Map([ + ['api-1', ''], + ]); + + const result = await extractTransitiveDependencies( + client, store, testContext, '/output', policies, new Map(), [], [] + ); + + expect(result.errorCount).toBe(0); + expect(result.extractedDescriptors).toContainEqual( + expect.objectContaining({ type: ResourceType.NamedValue, nameParts: ['demo-backend-id'] }) + ); + expect(client.getResource).not.toHaveBeenCalledWith( + testContext, + expect.objectContaining({ type: ResourceType.Backend, nameParts: ['{{demo-backend-id}}'] }) + ); + }); + }); }); diff --git a/tests/unit/services/transitive-resolver.test.ts b/tests/unit/services/transitive-resolver.test.ts index 8d05947e..68671a20 100644 --- a/tests/unit/services/transitive-resolver.test.ts +++ b/tests/unit/services/transitive-resolver.test.ts @@ -45,6 +45,21 @@ describe('transitive-resolver', () => { }); }); + it('should not treat named values or policy expressions as backend names', () => { + const policy = ` + + + + `; + const refs = scanPolicyReferences(policy); + + expect(refs).toContainEqual({ + type: ResourceType.NamedValue, + name: 'demo-backend-id', + }); + expect(refs.filter((ref) => ref.type === ResourceType.Backend)).toHaveLength(0); + }); + it('should detect policy fragment references', () => { const policy = ''; const refs = scanPolicyReferences(policy);