fix(ui): let the sharing list print the server's key mask - #321
Merged
Merged
Conversation
`GET /api/sharings` already returns a finished, masked `key` per row -- `sharing_row` decrypts the stored ciphertext and hands it to `mask_upstream_key` (first 2 chars + `-` + last 4; `****` when the stored key is <= 6 chars or decryption fails). The browser never holds the plaintext, so the sharing list's local `maskKey` could only re-process a value that was already masked. It did so badly. `maskKey` was `key.slice(0, 3) + "****" + key.slice(-4)` for every input: its `key.length <= 8` branch returned the same string as the other branch (dead since the `d70e032` prototype -- the mock keys there were all longer than 8 chars), it dropped one character of the server prefix (3 vs 2), and on the server's whole-string fallback it printed eleven stars where the server wrote four. Print the row's own field instead, which is what the settings API-Key table next door already does (`renderSettings` prints `/api/api-keys`'s masked `key` as-is). One fact, one implementation. The new gate `state_gate::the_sharing_list_prints_the_mask_the_server_made` keeps it that way. Four rules, each with its own tooth: 1. the cell prints a bare member expression (not another call); 2. that field is the one `sharingsToView` passes through verbatim; 3. the server really does write the mask into it -- in the function that builds the row, the value's binding reaches the `fn` that returns the all-stars literal, and the fallback literal sits in the same body (this rule goes red the day the server stops masking, because then "print it as-is" becomes a leak); 4. `app.js`'s code carries no second implementation -- not one occurrence of the server's mask literal. The mask function name and the mask literal are both derived from `sharing.rs`; nothing is snapshotted. Scope is lexical: the gate proves whose value the cell prints and where that value comes from, not the pixels (CI has no JS runner), which `ui/README.md` records in the new section. A/B on the pre-fix tree: the gate reports exactly `r1=false r2=true r3=true r4=false` -- the frontend was double-masking and carried a second implementation while the mapper and the server were never wrong. Verified on the branch tree: `cargo test` 421 passed / 0 failed (baseline 417, +4 new tests), `cargo fmt --check` and `cargo clippy --all-targets -D warnings` both exit 0, `node --check ui/js/app.js` clean.
Owner
Author
|
Self-review (the Checked against the merged tree:
|
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.
Summary
GET /api/sharingsalready returns a finished, maskedkeyper row(
sharing_rowdecrypts the stored ciphertext and hands it tomask_upstream_key), so the sharing list's localmaskKeywas onlyre-processing a value that was already masked — a second implementation of one
fact. It also got it wrong: its
key.length <= 8branch returned the samestring as the other branch, it printed a 3-character prefix where the server
prints 2, and on the server's whole-string fallback it printed eleven stars
where the server wrote four.
The list now prints the row's own field, the same rule the settings API-Key
table already follows.
A new lexical gate (
state_gate::the_sharing_list_prints_the_mask_the_server_made)holds four rules, each with its own tooth: the cell prints a bare member
expression; that field is the one the row mapper passes through verbatim; the
server really does write the mask into it (so the rule goes red the day the
server stops masking); and
app.jscarries no second implementation of themask literal.
Related Issue
Changes
ui/js/app.js: drop the localmaskKey, prints.key(the server value) in#share-body; comment records the server's mask semanticssrc/state_gate.rs: gate + roster (positive control) + teeth (four variants) + scanner self-testsui/index.html: bump theapp.jscache-bust tokenui/README.md: the data-section claim now names the server mask, plus a new section documenting the axis and the gate's scopeTests
cargo test全部通过 — 421 passed / 0 failed (baseline 417, +4 new tests)cargo fmt --check通过 — exit 0cargo clippy --all-targets -- -D warnings— exit 0node --check ui/js/app.js— cleanA/B on the pre-fix tree (
git show HEAD:ui/js/app.js): the gate reportsr1=false r2=true r3=true r4=false— the frontend was double-masking andcarrying a second implementation, while the mapper and the server were never
wrong.
Checklist
fix/)