fix(payment-connector): validate secret key formats client-side at add time#1573
Conversation
…d time Adding a payment connector with a wrong-format secret (e.g. a non-Ed25519 CoinbaseCDP apiKeySecret) succeeded at `add` — the TUI even showed a green tick — and only failed later at deploy with an opaque server-side error. Add a shared validator module (src/cli/primitives/payment-validation.ts) following the validateBYOMountPath pattern: each validator returns `true | string` so it plugs into both the CLI add path and the TUI SecretInput `customValidation` prop, validating identically. - apiKeySecret (CoinbaseCDP): base64-encoded Ed25519 private key - walletSecret (CoinbaseCDP): base64-encoded EC P-256 private key - authorizationPrivateKey (StripePrivy): base64-encoded EC P-256, accepting the optional wallet-auth: prefix (now via the shared stripWalletAuthPrefix) Opaque identifier fields (apiKeyId, appId, appSecret, authorizationId) keep their non-empty checks since they have no documented format. Ed25519 and P-256 keys differ in size, so each gets its own decoded-byte band rather than reusing one range. Integration-test fixtures now use real keys.
|
Claude Security Review: no high-confidence findings. (run) |
Package TarballHow to installgh release download pr-1573-tarball --repo aws/agentcore-cli --pattern "*.tgz" --dir /tmp/pr-tarball
npm install -g /tmp/pr-tarball/aws-agentcore-0.20.0.tgz |
agentcore-cli-automation
left a comment
There was a problem hiding this comment.
Looks good to merge.
Reviewed the changes — the shared validator module is well-factored, plugs into both the CLI and TUI surfaces with the same true | string contract, and correctly addresses the #1564 repro by failing at add time instead of deploy. The Ed25519 vs P-256 byte-band split is well-justified in the comments (reusing one band would have rejected valid CDP keys).
Tests use real generated keys (via generateKeyPairSync) rather than mocked buffers, including good cross-curve rejection coverage. Integration fixtures were updated consistently to use real keys so lifecycle tests don't trip the new format check.
No serious issues found.
Coverage Report
|
notgitika
left a comment
There was a problem hiding this comment.
_o
.#$?\
.'##$ ?$\
.'.###$ ?$$\
.' .####$ ?$$$\
.' +#####$ `$$$$$\
.' d######$ `$$$$$$\
.' .########$ ?$$$$$$$\
.' .#########$ ?$$$$$$$$\
.' ,##########$ ?$$$$$$$$$\
.' +###########$ `?$$$$$$$$$$\
..' .;############$ `$$$$$$$$$$$$\
|' ; ````""""?$ ?$$$$??"""?$$$\
|_/_____;._______._____$_________)?;__________?;X\___.-.
/'_____;_|______,|ooooo$oo|o,,,,/..|..___ |`'+\.\.'
.'|| ,ooooo|ooooodS|SSSSSSSS|SSS/'SSS|SSSSSSSSSSS|SSSS|/
. .; |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/'
.,;.;';`'(`;|',.;'.'.,,,+','+.,'+,.+,'+..^+,','+.,',.';.'.,+'`;'.';,:'';,:.'
;'.';,:'.,;.;';`'(`;|',.;'.'.,,,+','+.,'+,.+,'+..^+,','+.,',.';.'.,+'`.';,:'
Description
Adds client-side format validation for payment connector secret keys at
addtime, so a wrong-format key is caught immediately instead of failing later atdeploy.Bug (#1564): A user entered a wrong-format CoinbaseCDP
apiKeySecretin the TUI, saw a green-tick "valid", andadd payment-connectorsucceeded — butdeploythen failed server-side withPayment API error (400): Invalid apiKeySecret format: Expected base64-encoded Ed25519 private key.Root cause: Only the StripePrivy
authorizationPrivateKeyhad any format validation, and it lived inline in the CLI action — the TUI never ran it. Every other secret field (including the CoinbaseCDP key fields) was accepted as any non-empty string on both surfaces, so invalid keys were written to.env.localand only rejected by the service at deploy.Fix: A shared validator module (
src/cli/primitives/payment-validation.ts) following the existingvalidateBYOMountPathpattern — each validator returnstrue | string, which plugs directly into both the CLI add path and the TUISecretInputcustomValidationprop, so the two surfaces validate identically (the same approach used by the session-storage path validation in #1555).Validated fields (cryptographic, server-enforced format):
apiKeySecret(CoinbaseCDP) — base64-encoded Ed25519 private keywalletSecret(CoinbaseCDP) — base64-encoded EC P-256 private keyauthorizationPrivateKey(StripePrivy) — base64-encoded EC P-256, accepting the optionalwallet-auth:prefix (now via the sharedstripWalletAuthPrefix)Opaque identifier fields (
apiKeyId,appId,appSecret,authorizationId) keep their non-empty checks, since they have no documented format and over-validating risks false rejections. Ed25519 and P-256 keys differ substantially in size (Ed25519 decodes to 32–64 bytes; P-256 PKCS8 to ~138), so each field uses its own decoded-byte band rather than one shared range — reusing the P-256 band for the Ed25519 field would have rejected every valid CDP key.Related Issue
Closes #1564
Documentation PR
N/A — no doc changes; validation behavior is self-describing via the error messages.
Type of Change
Testing
How have you tested the change?
npm run test:unitandnpm run test:integnpm run typechecknpm run lintsrc/assets/, I rannpm run test:update-snapshotsand committed the updated snapshotsNotes:
typecheck(0 errors),lint(0 errors),test:unit(5404 passed),test:integ(274 passed) all green. Nosrc/assets/changes.payment-validation.test.tsexercise each validator against real generated Ed25519/P-256 keys, including cross-curve rejection (a P-256 key failsvalidateApiKeySecret, an Ed25519 key failsvalidateWalletSecret) and thewallet-auth:prefix.add-remove-payment.test.ts) now use real keys so the lifecycle tests exercise add/remove rather than tripping the new format check.--api-key-secret randomsecret) is now rejected atadd(apiKeySecret must be a base64-encoded Ed25519 private key (unexpected length), exit 1, connector not written), while valid Ed25519 + P-256 keys are accepted.Checklist
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.