feat(vault): plumb the PAM partialData gating marker through the cipher model - #22169
Closed
Hinton wants to merge 1 commit into
Closed
feat(vault): plumb the PAM partialData gating marker through the cipher model#22169Hinton wants to merge 1 commit into
Hinton wants to merge 1 commit into
Conversation
…er model The server withholds a PAM-gated cipher's sensitive fields and returns a reduced blob instead - the encrypted name plus, for logins, the encrypted URIs. This carries that blob from the wire down to CipherView, where UI surfaces can use its presence as the "this row is gated" signal. - CipherResponse reads PartialData (accepting a string or an already-parsed object) and lifts the encrypted Name onto `name` so the normal decrypt path handles it. For logins it also lifts Uris, building the login from Uris alone so an over-sharing blob can never leak a password or TOTP onto a gated view. A malformed blob keeps the marker and drops the name - failing to parse must not un-gate the row. - The marker is persisted through CipherData -> Cipher -> CipherView. - CipherRecordMapper.shouldInclude hides gated rows from the SDK, which has no partial-data decrypt path and rejects sparse ciphers. They stay in client state. - DefaultCipherEncryptionService re-attaches the marker after the SDK round trip drops it, on both the single and bulk decrypt paths. Note this is additive to the existing `data` field, which carries the full blob-encrypted payload; the two are unrelated. Also adds `leaseGated`, the transient companion marker for a full cipher served under an active lease. It is deliberately never persisted or serialized, and has no producer until the PAM leased-cipher fetcher lands - declared here because the cipher-view banner seam forwards it.
This was referenced Jul 31, 2026
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## pam/sdk-record-should-include #22169 +/- ##
================================================================
Coverage ? 52.73%
================================================================
Files ? 4203
Lines ? 133184
Branches ? 20966
================================================================
Hits ? 70230
Misses ? 57816
Partials ? 5138 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Hinton
added a commit
that referenced
this pull request
Aug 6, 2026
…pivot) Pivots the PAM cipher-gating client work: instead of hiding gated rows from the SDK and hand-decrypting them, gated rows now flow through the SDK, which decrypts the reduced envelope into a `partial` view (bitwarden/sdk-internal#1359). - Revert the SdkRecordMapper `shouldInclude` filter and the `decryptPartialCiphers` path (the last caller of the deprecated `Cipher.decrypt`). Gated rows go through `decryptMany*` like any cipher. - CipherResponse passes `partialData` through verbatim (no client lift); `Cipher.toSdkCipher`/`fromSdkCipher` map it both ways so it round-trips losslessly (re-attach hack removed). The decrypted view's gating marker is the SDK's `partial` boolean. - Vault seams read `view.partial`; `CipherOpenVerdict` gains a `handled` case so the open gate can block the open and surface the "Privileged Controls license required" dialog (unlicensed-user design). - Move the "Privileged" badge into a dedicated "Controlled access" column, shown only when a PAM-enabled org (`Organization.usePam`) is in view and the badge seam is provided. Depends on sdk-internal#1359 (adds Cipher.partial_data + the `partial` view flag + the restricted decrypt path); the `@bitwarden/sdk-internal` bump lands once that publishes. Supersedes #22168, #22169, #22170.
Member
Author
Hinton
added a commit
that referenced
this pull request
Aug 6, 2026
…pivot) Pivots the PAM cipher-gating client work: instead of hiding gated rows from the SDK and hand-decrypting them, gated rows now flow through the SDK, which decrypts the reduced envelope into a `partial` view (bitwarden/sdk-internal#1359). - Revert the SdkRecordMapper `shouldInclude` filter and the `decryptPartialCiphers` path (the last caller of the deprecated `Cipher.decrypt`). Gated rows go through `decryptMany*` like any cipher. - CipherResponse passes `partialData` through verbatim (no client lift); `Cipher.toSdkCipher`/`fromSdkCipher` map it both ways so it round-trips losslessly (re-attach hack removed). The decrypted view's gating marker is the SDK's `partial` boolean. - Vault seams read `view.partial`; `CipherOpenVerdict` gains a `handled` case so the open gate can block the open and surface the "Privileged Controls license required" dialog (unlicensed-user design). - Move the "Privileged" badge into a dedicated "Controlled access" column, shown only when a PAM-enabled org (`Organization.usePam`) is in view and the badge seam is provided. Depends on sdk-internal#1359 (adds Cipher.partial_data + the `partial` view flag + the restricted decrypt path); the `@bitwarden/sdk-internal` bump lands once that publishes. Supersedes #22168, #22169, #22170.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🎟️ Tracking
PAM cipher gating, 2/4. Stacked on #22168 (needs its
shouldIncludehook).Server side: bitwarden/server#8114 (field) and #8115 (behavior).
📔 Objective
The server withholds a PAM-gated cipher's sensitive fields and returns a reduced blob
instead — the encrypted name plus, for logins, the encrypted URIs. This carries that blob
from the wire down to
CipherView, where UI surfaces can use its presence as the "this rowis gated" signal.
CipherResponsereadsPartialData(accepting a string or an already-parsed object)and lifts the encrypted
Nameontonameso the normal decrypt path handles it. Forlogins it also lifts
Uris, building the login fromUrisalone — a deliberateallowlist, so an over-sharing blob can never leak a password or TOTP onto a gated view.
A malformed blob keeps the marker and drops the name: failing to parse must not un-gate
the row.
CipherData→Cipher→CipherView.CipherRecordMapper.shouldIncludehides gated rows from the SDK (no partial-datadecrypt path; it rejects sparse ciphers). They stay in client state.
DefaultCipherEncryptionServicere-attaches the marker after the SDK round tripdrops it, on both the single and bulk decrypt paths.
Note for reviewers
This is additive to the existing
datafield, which carries the full blob-encryptedpayload (PM-32696). The two are unrelated and both are kept.
Also adds
leaseGated, the transient companion marker for a cipher served under activeaccess. It is deliberately never persisted to
CipherDatanor serialized infromJSON,and has no producer yet — declared here because the cipher-view banner seam (#22171)
forwards it.
Verification
npm test -- libs/common/src/vault libs/common/src/platform/services/sdk— 784 passedacross 47 suites.
npm run test:typesclean.New
cipher.response.spec.tscovers the name/URI lift, that no secret login field is everlifted, the malformed-blob and already-parsed-object paths, and that a real top-level
Namewins over the blob's.
🚨 Breaking Changes
None — purely additive. With no server sending
PartialData,partialDatais alwaysabsent and every path behaves exactly as before.