fix(web): wire force_unsafe to Add Memory privacy-confirm dialog#789
Merged
fix(web): wire force_unsafe to Add Memory privacy-confirm dialog#789
Conversation
When ``ca483c5`` applied the trust-boundary ``privacy.enforce_write_guard``
to ``POST /api/add`` with ``force_unsafe: bool = False`` defaulting to
block, the SPA's existing privacy-warning flow in ``app.js`` was not
updated to thread the bypass flag through the resulting submit. Effect:
when the user pasted secret-shaped content (or tripped a false positive),
the SPA showed the confirm dialog as before, but the confirmed POST went
to the server *without* ``force_unsafe`` — and the server matched the
same patterns the client just surfaced and returned 403. The dialog
became a no-op trap.
Wire ``forceUnsafe = true`` from a confirmed dialog through to the
request body so the SPA's "I know, store it anyway" path actually
reaches the bypass branch the server expects:
let forceUnsafe = false;
if (patterns.some(...)) {
if (!await showConfirm(...)) return;
forceUnsafe = true;
}
...
const body = { content, ..., namespace };
if (forceUnsafe) body.force_unsafe = true;
await api('POST', '/api/add', body);
Clean content still posts without the field (server default ``False``
keeps blocking semantics intact). Cancel still aborts. The new comment
block records *why* the wiring exists so a future cleanup pass does not
silently drop it.
Pin three branches in ``tests-js/add-memory-force-unsafe.test.mjs``:
1. clean content → POST without ``force_unsafe``
2. flagged content + confirm → POST with ``force_unsafe: true``
3. flagged content + cancel → no POST
Mutation-validated per ``feedback_pin_test_mutation_validation.md``:
reverting the ``body.force_unsafe = true`` line makes case (2) fail with
``expected undefined to be true`` so the regression cannot land silently.
Bump ``app.js?v=99`` → ``?v=100`` per ``feedback_static_asset_cache_bust.md``;
disk-cached SPAs would otherwise miss the wiring on first load.
Out of scope (separate follow-up):
- ``PATCH /api/chunks/{id}`` (lines 2151, 3621) and ``POST /api/upload``
(lines 3875, 5405) accept ``force_unsafe`` server-side but the SPA
has no UI for it. Those are *new gaps* — never had a confirm flow —
so they need a different pattern (detect 403 ``redaction_blocked``,
show retry-confirm dialog) and warrant a shared helper. Tracked
separately so this PR stays a single regression fix.
Co-Authored-By: Claude <[email protected]>
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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
Fix a regression introduced by
ca483c5. When the trust-boundaryprivacy.enforce_write_guardwas wired toPOST /api/addwithforce_unsafe: bool = Falsedefaulting to block, the SPA's existing privacy-warning flow inapp.jswas not updated to thread the bypass flag through the resulting submit.Result: confirmed Add Memory submissions of secret-shaped content (or false positives) now get a server-side 403 instead of being saved. The confirm dialog became a no-op trap.
This PR threads
forceUnsafe = truefrom a confirmed dialog into the POST body so the user's "I know, store it anyway" choice actually reaches the bypass branch the server already supports.What changed
web/static/app.js— Add Memory submit handler:web/static/index.html—?v=99→?v=100perfeedback_static_asset_cache_bust.md.tests-js/add-memory-force-unsafe.test.mjs— three pinned branches:force_unsafeforce_unsafe: trueMutation-validated per
feedback_pin_test_mutation_validation.md: revertingbody.force_unsafe = truemakes case (2) fail withexpected undefined to be true, so the regression cannot silently re-land.Out of scope (follow-up)
PATCH /api/chunks/{id}andPOST /api/uploadacceptforce_unsafeserver-side but the SPA has no UI for it (lines 2151 / 3621 / 3875 / 5405 inapp.js). Those are new gaps, not regressions — they never had a confirm flow — and need a different pattern (detect403 redaction_blocked, show retry-confirm dialog) plus likely a shared helper. Will track in a separate issue so this PR stays a single regression fix perfeedback_one_change_per_pr.md.Test plan
npx vitest run— 14/14 pass (5 test files)sk-token into Add Memory → confirm dialog appears → confirm → save succeeds → row appears in resultsReferences
web/routes/system.py:1203(enforce_write_guard(surface="web_api_add")).web/schemas/memory.py:14-18(theforce_unsafe: bool = Falsefield this PR finally exercises from the SPA).🤖 Generated with Claude Code