From 686ab5a9ec7d21cb8e3cc25556fd91ed73ef5804 Mon Sep 17 00:00:00 2001 From: notgitika Date: Sun, 21 Jun 2026 23:02:38 -0400 Subject: [PATCH 1/3] 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); } From 516527723d31bf3f41a43f904eaf7fb3e75eb15a Mon Sep 17 00:00:00 2001 From: notgitika Date: Mon, 29 Jun 2026 10:40:55 -0400 Subject: [PATCH 2/3] fix: require project when name is passed with --arn, cross-check region Addresses review feedback: - requireProject() is now called whenever a config name is provided, even alongside --arn, so the user gets a clear error outside a project. - Region mismatch between name resolution and ARN is now rejected, closing the same class of silent inconsistency on the region axis. --- src/cli/commands/pause/command.tsx | 4 ++-- src/cli/operations/eval/pause-resume.ts | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/cli/commands/pause/command.tsx b/src/cli/commands/pause/command.tsx index d790fe191..c42f8482a 100644 --- a/src/cli/commands/pause/command.tsx +++ b/src/cli/commands/pause/command.tsx @@ -35,7 +35,7 @@ function registerOnlineEvalSubcommand(parent: Command, action: 'pause' | 'resume process.exit(1); } - if (!cliOptions.arn) { + if (!cliOptions.arn || name) { requireProject(); } @@ -126,7 +126,7 @@ function registerOnlineInsightsSubcommand(parent: Command, action: 'pause' | 're process.exit(1); } - if (!cliOptions.arn) { + if (!cliOptions.arn || name) { requireProject(); } diff --git a/src/cli/operations/eval/pause-resume.ts b/src/cli/operations/eval/pause-resume.ts index 083e498a1..7681da6c7 100644 --- a/src/cli/operations/eval/pause-resume.ts +++ b/src/cli/operations/eval/pause-resume.ts @@ -94,6 +94,17 @@ async function resolveConfig( `Pass only one, or pass matching values.`, }; } + + if (nameResolution.region !== arnResolution.region) { + return { + success: false, + error: + `--arn and config name "${options.name}" resolve to different regions ` + + `(name resolves to "${nameResolution.region}", ARN resolves to "${arnResolution.region}"). ` + + `Pass only one, or use --region to override.`, + }; + } + return arnResolution; } if (options.arn) { From fe3b0503dde30228ef497b87b57fab9ad81ef257 Mon Sep 17 00:00:00 2001 From: notgitika Date: Mon, 29 Jun 2026 10:54:09 -0400 Subject: [PATCH 3/3] test: add region mismatch test for name + ARN cross-validation --- .../operations/eval/__tests__/pause-resume.test.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/cli/operations/eval/__tests__/pause-resume.test.ts b/src/cli/operations/eval/__tests__/pause-resume.test.ts index f207b7ef4..f9babc41a 100644 --- a/src/cli/operations/eval/__tests__/pause-resume.test.ts +++ b/src/cli/operations/eval/__tests__/pause-resume.test.ts @@ -204,6 +204,17 @@ describe('handlePauseResume', () => { expect(mockUpdateOnlineEvalExecutionStatus).not.toHaveBeenCalled(); }); + it('rejects mismatched regions between name and ARN', async () => { + mockLoadDeployedProjectConfig.mockResolvedValue(makeContext('my-config', 'cfg-123', 'dev', 'us-west-2')); + + 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.error.message).toContain('different regions'); + 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'));