Skip to content

fix: tighten sanitize logic on payment secrets#1631

Open
nborges-aws wants to merge 1 commit into
mainfrom
sanitize-payments
Open

fix: tighten sanitize logic on payment secrets#1631
nborges-aws wants to merge 1 commit into
mainfrom
sanitize-payments

Conversation

@nborges-aws

Copy link
Copy Markdown
Contributor

Description

Update payment error messaging in order to strengthen the redaction logic of secret values. Previously, we sanitized based on key names in key:value pairs. This meant that the secret being nested in any unexpected way or shape changes would lead to secrets slipping through in logging.

This PR updates this logic in the following ways:

  • Sanitize error body by values, rather than keys -> this guarantees no matter what format or nesting level the secret is included at, the value will be redacted. This is done using new sanitizeErrorBody helper method.
  • Add gating logic based on debug mode [optional change] -> Part of request for this change was to hide error body logging behind debug mode. Without debug mode: users will see base errorMsg + error code. With debug mode: redacted error body will be included.
  • Added rethrowWithContext helper method. Ensures that call sites who rethrow errors maintain the error code provided from original exception.

Related Issue

Closes #

Documentation PR

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update
  • Other (please describe):

Testing

How have you tested the change?

  • I ran npm run test:unit and npm run test:integ
  • I ran npm run typecheck
  • I ran npm run lint
  • If I modified src/assets/, I ran npm run test:update-snapshots and committed the updated snapshots

Checklist

  • I have read the CONTRIBUTING document
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the
terms of your choice.

@nborges-aws nborges-aws requested a review from a team June 24, 2026 16:01
@github-actions github-actions Bot added the size/m PR size: M label Jun 24, 2026
@github-actions github-actions Bot added the agentcore-harness-reviewing AgentCore Harness review in progress label Jun 24, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Jun 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Package Tarball

aws-agentcore-0.20.2.tgz

How to install

gh release download pr-1631-tarball --repo aws/agentcore-cli --pattern "*.tgz" --dir /tmp/pr-tarball
npm install -g /tmp/pr-tarball/aws-agentcore-0.20.2.tgz

@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Jun 24, 2026

@agentcore-cli-automation agentcore-cli-automation left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tightening this up — value-based redaction is a clear improvement over the key-name regex, and rethrowWithContext is a nice helper. A few things worth addressing before merge, mainly around the default (non-DEBUG) UX and a couple of consistency gaps. Nothing fundamental; happy to re-review once you take a look.

const errorBody = await response.text().catch(() => '');
const baseMsg = `Payment API error (${response.status})`;
const debugExcerpt = sanitizeErrorBody(errorBody, secretsToRedact);
const error = new Error(debugExcerpt ? `${baseMsg}: ${debugExcerpt}` : baseMsg) as Error & {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default-mode error message loses all human-readable detail.

With DEBUG unset (the common case for end users), sanitizeErrorBody returns '' and the thrown error's .message becomes just Payment API error (400) — no validation reason, no server-side message, nothing actionable. The PR description says the user will see "base errorMsg + error code", but error.code lives on the .code property and is not surfaced in .message, so unless the consuming UI specifically reads .code (today, setupPaymentCredentialProviders just pushes the raw Error into result.errors, and status/action.ts calls getErrorMessage(error)), users will see a status code with no hint as to what went wrong. Compared to the existing behavior — which at least showed the (partially redacted) server message — this is a real diagnostic regression for the most common path.

A few ways to address this (any one would help, can combine):

  1. Always append the parsed code/__type to the base message. The code itself is not sensitive (e.g. ValidationException, ThrottlingException) and immediately tells the user the failure class. Something like Payment API error (400) ValidationException even when DEBUG is off.
  2. Extract message/Message/errorMessage from the JSON body and include it (run through the same value-based redactor) even when DEBUG is off. Server-provided messages typically contain the actionable detail ("name must match pattern …", "resource already exists", etc.) and the value-based redaction guarantees no echoed secret slips through.
  3. Gate only the raw body excerpt behind DEBUG, but always include the parsed code + message field. Best of both — full body only with DEBUG, but structured/redacted message always shown.

if (msg.includes('(404)') || msg.includes('ResourceNotFoundException')) return null;
const code = (err as { code?: unknown }).code;
if (code === 'ResourceNotFoundException' || msg.includes('(404)')) return null;
throw new Error(`Failed to get payment credential provider "${options.name}": ${msg}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.code is dropped on rethrow here (and at line 340 in getPaymentManager).

The PR description says rethrowWithContext was added to "ensure call sites who rethrow errors maintain the error code provided from original exception," but these two non-404 rethrow paths still use throw new Error(\...: ${msg}`)which discardserr.code. For consistency (and so downstream callers can rely on the code being present regardless of which payment function threw), these should also go through rethrowWithContext`:

throw rethrowWithContext(`Failed to get payment credential provider "${options.name}"`, err);

No current caller depends on .code from these two functions, so it's not a live bug — just a contract gap that future callers will trip on.

}
}
return out.slice(0, 500);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given how security-sensitive sanitizeErrorBody is (a regression here means secrets leak), it would be worth adding direct unit tests for agentcore-payments.ts covering at minimum:

  • DEBUG unset → returns '' regardless of body / secrets
  • DEBUG set, secret appears verbatim, snake_cased server echo, nested in JSON, embedded in free-text → all replaced with [REDACTED]
  • Empty/undefined secrets iterable → no crash, body still capped at 500 chars
  • Truncation happens after redaction (so a secret straddling the 500-char boundary still gets redacted)

There are currently no unit tests for this file at all (only the mocked tests in pre-deploy-payments.test.ts), so the redactor's correctness is effectively untested. A small focused test would let future refactors here move with confidence.

@github-actions github-actions Bot removed the agentcore-harness-reviewing AgentCore Harness review in progress label Jun 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 37.01% 13555 / 36617
🔵 Statements 36.28% 14412 / 39715
🔵 Functions 31.67% 2327 / 7346
🔵 Branches 30.77% 8926 / 29004
Generated in workflow #3798 for commit 8770806 by the Vitest Coverage Report Action

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m PR size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants