Summary
Two latent flaws that #741 fixed for @cipherstash/stack/wasm-inline remain on the native entry, where the same code shapes exist unguarded.
Raised as finding 11/15 in the review of #741.
1. No withResult call site passes onException
Already tracked in #743 for the whole repo — repeated here only because it is one of the two things #741 fixed locally and left unfixed natively. A non-Error rejection still becomes Error("Something went wrong") in packages/stack/src/encryption/operations/* and packages/protect/src/ffi/operations/*.
2. Native bulk ops match FFI results positionally, unguarded
packages/stack/src/encryption/operations/bulk-decrypt.ts and its siblings pair FFI results to inputs by index with no length assertion. A short response makes decryptResult undefined, and:
// bulk-decrypt.ts:49
if ('error' in decryptResult) {
throws TypeError: Cannot use 'in' operator ... undefined — a bewildering error that names nothing about the real problem. The WASM entry now fails closed here with a message naming the counts (assertBatchLength).
3. Divergent partial-failure contract between the two entries
Native bulkDecrypt returns per-item { id, data } / { id, error }, preserving partial success. The WASM one collapses any failure into a single { failure } listing every bad index. Same operation name, different contracts — worth a deliberate decision rather than an accident of two implementations.
Suggested shape
Hoist the three helpers #741 added — toError, toFailure, assertBatchLength — into @/encryption/helpers and use them from both entries, so the guarantees are shared rather than re-derived. That also removes the risk of the next batch method on either side omitting the length assert.
Not urgent
Nothing here is a live incident: the FFI does not currently return short batches, and non-Error rejections are rare on the native path (protect-ffi throws real ProtectError instances there — unlike the WASM build, which ships no error class, which is why #741 hit it first).
Related
Summary
Two latent flaws that #741 fixed for
@cipherstash/stack/wasm-inlineremain on the native entry, where the same code shapes exist unguarded.Raised as finding 11/15 in the review of #741.
1. No
withResultcall site passesonExceptionAlready tracked in #743 for the whole repo — repeated here only because it is one of the two things #741 fixed locally and left unfixed natively. A non-
Errorrejection still becomesError("Something went wrong")inpackages/stack/src/encryption/operations/*andpackages/protect/src/ffi/operations/*.2. Native bulk ops match FFI results positionally, unguarded
packages/stack/src/encryption/operations/bulk-decrypt.tsand its siblings pair FFI results to inputs by index with no length assertion. A short response makesdecryptResultundefined, and:throws
TypeError: Cannot use 'in' operator ... undefined— a bewildering error that names nothing about the real problem. The WASM entry now fails closed here with a message naming the counts (assertBatchLength).3. Divergent partial-failure contract between the two entries
Native
bulkDecryptreturns per-item{ id, data }/{ id, error }, preserving partial success. The WASM one collapses any failure into a single{ failure }listing every bad index. Same operation name, different contracts — worth a deliberate decision rather than an accident of two implementations.Suggested shape
Hoist the three helpers #741 added —
toError,toFailure,assertBatchLength— into@/encryption/helpersand use them from both entries, so the guarantees are shared rather than re-derived. That also removes the risk of the next batch method on either side omitting the length assert.Not urgent
Nothing here is a live incident: the FFI does not currently return short batches, and non-
Errorrejections are rare on the native path (protect-ffi throws realProtectErrorinstances there — unlike the WASM build, which ships no error class, which is why #741 hit it first).Related
onExceptiongap, repo-wide.