From 686ab5a9ec7d21cb8e3cc25556fd91ed73ef5804 Mon Sep 17 00:00:00 2001 From: notgitika Date: Sun, 21 Jun 2026 23:02:38 -0400 Subject: [PATCH] fix(pause-online-insights): reject mismatched --name and --arn When both a config name and --arn were passed to `agentcore pause/resume online-insights`, the ARN silently won and the name was ignored. If they referred to different configs, the wrong config was paused with no warning. Now we cross-check: if both are provided, look up the named config and verify its configId matches the ARN's. Mismatches return a clear error explaining which config each side resolves to. Passing only one (or matching values) continues to work. Bug bash item #15 (P1). --- .../eval/__tests__/pause-resume.test.ts | 39 +++++++++++++++++++ src/cli/operations/eval/pause-resume.ts | 25 +++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/cli/operations/eval/__tests__/pause-resume.test.ts b/src/cli/operations/eval/__tests__/pause-resume.test.ts index 9a6291a3d..f207b7ef4 100644 --- a/src/cli/operations/eval/__tests__/pause-resume.test.ts +++ b/src/cli/operations/eval/__tests__/pause-resume.test.ts @@ -176,4 +176,43 @@ describe('handlePauseResume', () => { expect(result.error.message).toContain('Could not extract config ID'); }); }); + + describe('name + ARN cross-validation', () => { + it('accepts matching name and ARN', async () => { + mockLoadDeployedProjectConfig.mockResolvedValue(makeContext('my-config', 'cfg-123')); + mockUpdateOnlineEvalExecutionStatus.mockResolvedValue({ + configId: 'cfg-123', + executionStatus: 'DISABLED', + status: 'ACTIVE', + }); + + const arn = 'arn:aws:bedrock-agentcore:us-east-1:123456789012:online-evaluation-config/cfg-123'; + const result = await handlePauseResume({ name: 'my-config', arn }, 'pause'); + + assert(result.success); + expect(result.executionStatus).toBe('DISABLED'); + }); + + it('rejects mismatched name and ARN', async () => { + mockLoadDeployedProjectConfig.mockResolvedValue(makeContext('my-config', 'cfg-123')); + + const arn = 'arn:aws:bedrock-agentcore:us-east-1:123456789012:online-evaluation-config/different-cfg'; + const result = await handlePauseResume({ name: 'my-config', arn }, 'pause'); + + assert(!result.success); + expect(result.error.message).toContain('refer to different configs'); + expect(mockUpdateOnlineEvalExecutionStatus).not.toHaveBeenCalled(); + }); + + it('returns name-lookup error when both are passed but name is unknown', async () => { + mockLoadDeployedProjectConfig.mockResolvedValue(makeContext('other-config', 'cfg-999')); + + const arn = 'arn:aws:bedrock-agentcore:us-east-1:123456789012:online-evaluation-config/cfg-999'; + const result = await handlePauseResume({ name: 'missing-config', arn }, 'pause'); + + assert(!result.success); + expect(result.error.message).toContain('missing-config'); + expect(result.error.message).toContain('not found'); + }); + }); }); diff --git a/src/cli/operations/eval/pause-resume.ts b/src/cli/operations/eval/pause-resume.ts index 3a6771a4b..083e498a1 100644 --- a/src/cli/operations/eval/pause-resume.ts +++ b/src/cli/operations/eval/pause-resume.ts @@ -68,11 +68,34 @@ function parseOnlineEvalConfigArn( } /** - * Resolve config ID and region from either a project config name or an ARN. + * Resolve config ID and region from a project config name, an ARN, or both. + * + * When both are provided, the named config is looked up and its configId is + * cross-checked against the ARN. A mismatch means the user passed a name and + * ARN that point to different configs — we reject rather than silently + * preferring one (the ARN previously won, so the name was a no-op). */ async function resolveConfig( options: OnlineEvalActionOptions ): Promise<{ success: true; configId: string; region: string } | { success: false; error: string }> { + if (options.arn && options.name) { + const arnResolution = parseOnlineEvalConfigArn(options.arn, options.region); + if (!arnResolution.success) return arnResolution; + + const nameResolution = await resolveOnlineEvalConfig(options.name); + if (!nameResolution.success) return nameResolution; + + if (nameResolution.configId !== arnResolution.configId) { + return { + success: false, + error: + `--arn and config name "${options.name}" refer to different configs ` + + `(name resolves to "${nameResolution.configId}", ARN resolves to "${arnResolution.configId}"). ` + + `Pass only one, or pass matching values.`, + }; + } + return arnResolution; + } if (options.arn) { return parseOnlineEvalConfigArn(options.arn, options.region); }