From 639bcbe28351512f4a23ac4466f78f3d7c677007 Mon Sep 17 00:00:00 2001 From: notgitika Date: Wed, 27 May 2026 15:16:04 -0400 Subject: [PATCH 1/4] fix: Migrate Python Dockerfile from bookworm to trixie (CVE-2026-42010) Switch base image from python:3.12-slim-bookworm to python:3.12-slim-trixie to address CVE-2026-42010 (GnuTLS RSA-PSK authentication bypass, CVSS 9.8). Debian Trixie (13) became stable on May 16, 2026 and includes the GnuTLS security fix (DLA-4595-1). Bookworm full support ends June 2026. sim: https://t.corp.amazon.com/D448133494 --- .../__tests__/__snapshots__/dockerfile-render.test.ts.snap | 4 ++-- src/assets/container/python/Dockerfile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/assets/__tests__/__snapshots__/dockerfile-render.test.ts.snap b/src/assets/__tests__/__snapshots__/dockerfile-render.test.ts.snap index 4bc6e9294..90bb1457d 100644 --- a/src/assets/__tests__/__snapshots__/dockerfile-render.test.ts.snap +++ b/src/assets/__tests__/__snapshots__/dockerfile-render.test.ts.snap @@ -1,7 +1,7 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`Dockerfile enableOtel rendering > renders opentelemetry-instrument CMD when enableOtel is true > Dockerfile-enableOtel-true 1`] = ` -"FROM public.ecr.aws/docker/library/python:3.12-slim-bookworm +"FROM public.ecr.aws/docker/library/python:3.12-slim-trixie RUN pip install --no-cache-dir uv @@ -41,7 +41,7 @@ CMD ["opentelemetry-instrument", "python", "-m", "main"] `; exports[`Dockerfile enableOtel rendering > renders plain python CMD when enableOtel is false > Dockerfile-enableOtel-false 1`] = ` -"FROM public.ecr.aws/docker/library/python:3.12-slim-bookworm +"FROM public.ecr.aws/docker/library/python:3.12-slim-trixie RUN pip install --no-cache-dir uv diff --git a/src/assets/container/python/Dockerfile b/src/assets/container/python/Dockerfile index 60a0e87fc..cb3569eff 100644 --- a/src/assets/container/python/Dockerfile +++ b/src/assets/container/python/Dockerfile @@ -1,4 +1,4 @@ -FROM public.ecr.aws/docker/library/python:3.12-slim-bookworm +FROM public.ecr.aws/docker/library/python:3.12-slim-trixie RUN pip install --no-cache-dir uv From c8e9ed98864a5751ea7cf8ebf69c05c992e7c007 Mon Sep 17 00:00:00 2001 From: notgitika Date: Wed, 27 May 2026 18:31:54 -0400 Subject: [PATCH 2/4] fix: update hardcoded bookworm reference in import fallback Dockerfile The inline Dockerfile generated during `agentcore import` for Container builds (when no starter-toolkit Dockerfile exists) still referenced bookworm, leaving users on that path vulnerable to CVE-2026-42010. --- src/cli/commands/import/actions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/commands/import/actions.ts b/src/cli/commands/import/actions.ts index 990fe006f..a40ca6031 100644 --- a/src/cli/commands/import/actions.ts +++ b/src/cli/commands/import/actions.ts @@ -383,7 +383,7 @@ export async function handleImport(options: ImportOptions): Promise Date: Wed, 27 May 2026 18:49:18 -0400 Subject: [PATCH 3/4] feat: warn during deploy when Dockerfile uses deprecated bookworm base image Emits a console warning during preflight validation if a Container agent's Dockerfile still references slim-bookworm, guiding existing customers to update for CVE-2026-42010. --- .../__tests__/preflight-container.test.ts | 43 ++++++++++++++++++- src/cli/operations/deploy/preflight.ts | 22 +++++++++- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/cli/operations/deploy/__tests__/preflight-container.test.ts b/src/cli/operations/deploy/__tests__/preflight-container.test.ts index 0bf4a7af7..6898bdfd2 100644 --- a/src/cli/operations/deploy/__tests__/preflight-container.test.ts +++ b/src/cli/operations/deploy/__tests__/preflight-container.test.ts @@ -1,10 +1,11 @@ import type { AgentCoreProjectSpec, DirectoryPath } from '../../../../schema'; import { validateContainerAgents } from '../preflight.js'; -import { existsSync } from 'node:fs'; +import { existsSync, readFileSync } from 'node:fs'; import { afterEach, describe, expect, it, vi } from 'vitest'; vi.mock('node:fs', () => ({ existsSync: vi.fn(), + readFileSync: vi.fn(), })); vi.mock('../../../../lib', () => ({ @@ -26,6 +27,7 @@ vi.mock('../../../../lib', () => ({ })); const mockedExistsSync = vi.mocked(existsSync); +const mockedReadFileSync = vi.mocked(readFileSync); const CONFIG_ROOT = '/project/agentcore'; @@ -44,6 +46,11 @@ describe('validateContainerAgents', () => { vi.clearAllMocks(); }); + // Default readFileSync to return a safe Dockerfile so the warning check doesn't fail on unrelated tests + function mockValidDockerfile(): void { + mockedReadFileSync.mockReturnValue('FROM public.ecr.aws/docker/library/python:3.12-slim-trixie\n'); + } + it('does nothing when there are no Container agents', () => { const spec = makeSpec([{ name: 'zip-agent', build: 'CodeZip', codeLocation: dir('agents/zip-agent') }]); @@ -53,6 +60,7 @@ describe('validateContainerAgents', () => { it('does nothing when Container agent has a valid Dockerfile', () => { mockedExistsSync.mockReturnValue(true); + mockValidDockerfile(); const spec = makeSpec([ { name: 'container-agent', build: 'Container', codeLocation: dir('agents/container-agent') }, @@ -72,6 +80,7 @@ describe('validateContainerAgents', () => { it('only validates Container agents and skips CodeZip agents', () => { mockedExistsSync.mockReturnValue(true); + mockValidDockerfile(); const spec = makeSpec([ { name: 'zip-agent', build: 'CodeZip', codeLocation: dir('agents/zip-agent') }, @@ -104,6 +113,7 @@ describe('validateContainerAgents', () => { it('checks for custom dockerfile name when specified', () => { mockedExistsSync.mockReturnValue(true); + mockValidDockerfile(); const spec = makeSpec([ { name: 'gpu-agent', build: 'Container', codeLocation: dir('agents/gpu'), dockerfile: 'Dockerfile.gpu' }, @@ -124,4 +134,35 @@ describe('validateContainerAgents', () => { expect(() => validateContainerAgents(spec, CONFIG_ROOT)).toThrow(/Dockerfile\.gpu not found/); }); + + it('warns when Dockerfile uses deprecated bookworm base image', () => { + mockedExistsSync.mockReturnValue(true); + mockedReadFileSync.mockReturnValue( + 'FROM public.ecr.aws/docker/library/python:3.12-slim-bookworm\nRUN pip install uv' + ); + const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined); + + const spec = makeSpec([{ name: 'my-agent', build: 'Container', codeLocation: dir('agents/my-agent') }]); + + expect(() => validateContainerAgents(spec, CONFIG_ROOT)).not.toThrow(); + expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('CVE-2026-42010')); + expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('my-agent')); + + warnSpy.mockRestore(); + }); + + it('does not warn when Dockerfile uses trixie base image', () => { + mockedExistsSync.mockReturnValue(true); + mockedReadFileSync.mockReturnValue( + 'FROM public.ecr.aws/docker/library/python:3.12-slim-trixie\nRUN pip install uv' + ); + const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined); + + const spec = makeSpec([{ name: 'my-agent', build: 'Container', codeLocation: dir('agents/my-agent') }]); + + expect(() => validateContainerAgents(spec, CONFIG_ROOT)).not.toThrow(); + expect(warnSpy).not.toHaveBeenCalled(); + + warnSpy.mockRestore(); + }); }); diff --git a/src/cli/operations/deploy/preflight.ts b/src/cli/operations/deploy/preflight.ts index 6577eb529..f603d8aa6 100644 --- a/src/cli/operations/deploy/preflight.ts +++ b/src/cli/operations/deploy/preflight.ts @@ -6,7 +6,7 @@ import { CdkToolkitWrapper, createCdkToolkitWrapper, silentIoHost } from '../../ import { checkBootstrapStatus, checkStacksStatus, formatCdkEnvironment } from '../../cloudformation'; import { cleanupStaleLockFiles } from '../../tui/utils'; import type { IIoHost } from '@aws-cdk/toolkit-lib'; -import { existsSync } from 'node:fs'; +import { existsSync, readFileSync } from 'node:fs'; import * as path from 'node:path'; export interface PreflightContext { @@ -198,6 +198,8 @@ export function validateContainerAgents(projectSpec: AgentCoreProjectSpec, confi errors.push( `Agent "${agent.name}": ${agent.dockerfile ?? DOCKERFILE_NAME} not found at ${dockerfilePath}. Container agents require a Dockerfile.` ); + } else { + warnDeprecatedBaseImage(dockerfilePath, agent.name); } } } @@ -206,6 +208,24 @@ export function validateContainerAgents(projectSpec: AgentCoreProjectSpec, confi } } +const DEPRECATED_BASE_IMAGES = ['slim-bookworm']; + +function warnDeprecatedBaseImage(dockerfilePath: string, agentName: string): void { + try { + const content = readFileSync(dockerfilePath, 'utf-8'); + for (const image of DEPRECATED_BASE_IMAGES) { + if (content.includes(image)) { + console.warn( + `Warning: Agent "${agentName}" Dockerfile uses a base image containing "${image}" which is affected by ` + + `CVE-2026-42010 (GnuTLS authentication bypass). Update the FROM line to use python:3.12-slim-trixie.` + ); + } + } + } catch { + // Non-fatal — if we can't read the file, the existing validation will handle it + } +} + /** * Builds the CDK project. */ From e4cb8a1f1e51dedb7e104a795baf226ffd784c83 Mon Sep 17 00:00:00 2001 From: notgitika Date: Thu, 28 May 2026 16:06:42 -0400 Subject: [PATCH 4/4] fix: scope deprecated base image warning to FROM lines and extract shared constant - Only match deprecated images on FROM lines to avoid false positives from comments, labels, or ENV vars - Make warning message generic ("Trixie-based variant") instead of Python-specific - Extract PYTHON_BASE_IMAGE constant to avoid duplication across template and import action --- src/cli/commands/import/actions.ts | 3 ++- src/cli/constants.ts | 5 +++++ .../deploy/__tests__/preflight-container.test.ts | 15 +++++++++++++++ src/cli/operations/deploy/preflight.ts | 15 +++++++++------ 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/cli/commands/import/actions.ts b/src/cli/commands/import/actions.ts index a40ca6031..f8fac62fb 100644 --- a/src/cli/commands/import/actions.ts +++ b/src/cli/commands/import/actions.ts @@ -9,6 +9,7 @@ import type { } from '../../../schema'; import { validateAwsCredentials } from '../../aws/account'; import { arnPrefix } from '../../aws/partition'; +import { PYTHON_BASE_IMAGE } from '../../constants'; import { ExecLogger } from '../../logging'; import { setupPythonProject } from '../../operations/python/setup'; import { executeCdkImportPipeline } from './import-pipeline'; @@ -383,7 +384,7 @@ export async function handleImport(options: ImportOptions): Promise { warnSpy.mockRestore(); }); + + it('does not warn when bookworm appears in a non-FROM line', () => { + mockedExistsSync.mockReturnValue(true); + mockedReadFileSync.mockReturnValue( + 'FROM public.ecr.aws/docker/library/python:3.12-slim-trixie\n# migrated from slim-bookworm\nRUN pip install uv' + ); + const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined); + + const spec = makeSpec([{ name: 'my-agent', build: 'Container', codeLocation: dir('agents/my-agent') }]); + + expect(() => validateContainerAgents(spec, CONFIG_ROOT)).not.toThrow(); + expect(warnSpy).not.toHaveBeenCalled(); + + warnSpy.mockRestore(); + }); }); diff --git a/src/cli/operations/deploy/preflight.ts b/src/cli/operations/deploy/preflight.ts index f603d8aa6..0c004002d 100644 --- a/src/cli/operations/deploy/preflight.ts +++ b/src/cli/operations/deploy/preflight.ts @@ -213,12 +213,15 @@ const DEPRECATED_BASE_IMAGES = ['slim-bookworm']; function warnDeprecatedBaseImage(dockerfilePath: string, agentName: string): void { try { const content = readFileSync(dockerfilePath, 'utf-8'); - for (const image of DEPRECATED_BASE_IMAGES) { - if (content.includes(image)) { - console.warn( - `Warning: Agent "${agentName}" Dockerfile uses a base image containing "${image}" which is affected by ` + - `CVE-2026-42010 (GnuTLS authentication bypass). Update the FROM line to use python:3.12-slim-trixie.` - ); + for (const line of content.split('\n')) { + if (!/^\s*FROM\s+/i.test(line)) continue; + for (const image of DEPRECATED_BASE_IMAGES) { + if (line.includes(image)) { + console.warn( + `Warning: Agent "${agentName}" Dockerfile uses a base image containing "${image}" which is affected by ` + + `CVE-2026-42010 (GnuTLS authentication bypass). Update the FROM line to use a Trixie-based variant.` + ); + } } } } catch {