Skip to content

DynamoDB v3: dotted-path column return type doesn't model the nested __source/__hmac split #747

Description

@tobyhede

Follow-up from #725 (EQL v3 in encryptedDynamoDB).

Summary

A v3 column declared under a dotted path is split correctly at runtime but the split is not modelled in the encryptModel / bulkEncryptModels return type. The type describes the nested object as untouched plaintext.

Detail

Given:

const users = encryptedTable('users', {
  'profile.ssn': types.TextEq('profile.ssn'),
})

Runtime (correct, tested):

input:  { pk: 'u#1', profile: { ssn: '123-45-6789' } }
stored: { pk: 'u#1', profile: { ssn__source: 'CT…', ssn__hmac: '…' } }

Type (EncryptedAttributes, packages/stack/src/dynamodb/types.ts:180-194):

result.data.profile              // typed { ssn: string }  ← the type's belief
result.data.profile.ssn          // typechecks, but undefined at runtime
result.data.profile.ssn__source  // TYPE ERROR — but this is the real value

Root cause

EncryptedAttributes iterates K in keyof T (the input-model keys) and rewrites a key only when K extends keyof V3Columns<Table>. The column-map key is the string 'profile.ssn'; the model has no top-level 'profile.ssn' key, only profile (an object). profile fails the match and falls through to the pass-through arm with its original plaintext type. This is the same defect the top-level EncryptedAttributes was created to fix — it's just unfixed for nested paths. Called out in the docblock at types.ts:175-178.

Top-level v3 columns are unaffected. v2 is unaffected by this specific gap (it doesn't apply EncryptedAttributes at all — see the separate v2-overload-return-type follow-up).

Impact

Cosmetic until hit: runtime is correct and tested, but a caller who wants profile.ssn__source (e.g. to build a GSI key condition) gets a type error, and profile.ssn typechecks to undefined.

Fix sketch

Make the mapped type detect a dot in the column key, parse 'profile.ssn' into a path via template-literal types, recurse into the nested object type, and rewrite the leaf to <leaf>__source (+ optional <leaf>__hmac) while preserving plaintext siblings — for arbitrary depth and for multiple dotted columns sharing a prefix. Fiddly recursive mapped-type work; deliberately deferred out of #725.

Acceptance

  • Type test in packages/stack/__tests__/dynamodb/client-compat.test-d.ts asserting the nested __source/__hmac keys exist and the leaf plaintext key does not, with plaintext siblings preserved.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions