You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The whole app has one ConfirmDialog, hardwired to the SQL editor's production-destructive-query confirm. Other features that mutate data roll their own silent execution.
Problem
src/components/layout/AppLayout.tsx:294-306 mounts one <ConfirmDialog> wired only to useResultStore.confirmDialog:
<ConfirmDialogisOpen={!!confirmDialog?.isOpen}title="⚠️ Destructive Query on Production"message="You are about to run a destructive query (DROP, DELETE, TRUNCATE, or ALTER) on a PRODUCTION database..."confirmLabel="Execute Anyway"cancelLabel="Cancel"dangeronConfirm={confirmExecution}onCancel={cancelExecution}/>
Multiple features have their own silent execution paths with no confirm:
The pattern is identical (destructive SQL, multi-statement, partial-apply on failure, no read_only gating) but the confirm is bypassed because they don't route through executeQuery via the resultStore. Each feature rolls its own dialog (or no dialog).
The cross-cutting audit (docs/audits/cross-cutting.md F11) flagged this as P1.
Files
src/components/layout/AppLayout.tsx:294-306 — single ConfirmDialog wired only to resultStore
src/components/common/ConfirmDialog.tsx:1-65 — primitive is generic but only used by AppLayout
src/components/designer/TableDesigner.tsx (no confirm path at all)
src/components/compare/SyncPreview.tsx (no confirm path at all)
src/components/backup/BackupDialog.tsx + RestoreDialog.tsx (no confirm path at all)
Repro
Designer: open CREATE TABLE for users, set charset, click Save → DDL executes immediately.
Compare: select two schemas, click "Execute Sync" → all generated statements run in sequence without confirmation.
Backup: select "Drop existing database before restore" → restore proceeds without confirming the drop.
Expected
A single useConfirm() hook backed by a confirmStore. The dialog is mounted once at AppLayout and shows whichever pending confirm is highest-priority. Each feature calls await useConfirm({ title, message, danger, confirmLabel }) before executing.
AppLayout renders <ConfirmDialog {...pendingConfirm} /> reading from the store.
Designer/Compare/Backup/Restore/Explain each call const ok = await useConfirmStore.getState().requestConfirm({ ... }); if (!ok) return; before executing.
Acceptance
Designer "Save" on a non-empty table without preview opens a single ConfirmDialog showing the DDL diff. Compare "Execute Sync" opens a confirm listing the destructive statements. Backup "Restore" opens a confirm with the source/target DB names. The same primitive is reused. Each feature's existing #issue closes (this is the unifying fix).
Context
The whole app has one ConfirmDialog, hardwired to the SQL editor's production-destructive-query confirm. Other features that mutate data roll their own silent execution.
Problem
src/components/layout/AppLayout.tsx:294-306mounts one<ConfirmDialog>wired only touseResultStore.confirmDialog:Multiple features have their own silent execution paths with no confirm:
The pattern is identical (destructive SQL, multi-statement, partial-apply on failure, no read_only gating) but the confirm is bypassed because they don't route through
executeQueryvia the resultStore. Each feature rolls its own dialog (or no dialog).The cross-cutting audit (docs/audits/cross-cutting.md F11) flagged this as P1.
Files
Repro
CREATE TABLEforusers, set charset, click Save → DDL executes immediately.Expected
A single
useConfirm()hook backed by aconfirmStore. The dialog is mounted once at AppLayout and shows whichever pending confirm is highest-priority. Each feature callsawait useConfirm({ title, message, danger, confirmLabel })before executing.Proposed fix
Scope M.
ConfirmDialogaccept arbitrarytitle,message,confirmLabel,cancelLabel,danger,onConfirm,onCancelprops (most already exist).src/stores/confirmStore.tswith{ pendingConfirm: { ... } | null, requestConfirm(opts) → Promise<boolean> }.AppLayoutrenders<ConfirmDialog {...pendingConfirm} />reading from the store.const ok = await useConfirmStore.getState().requestConfirm({ ... }); if (!ok) return;before executing.Acceptance
Designer "Save" on a non-empty table without preview opens a single ConfirmDialog showing the DDL diff. Compare "Execute Sync" opens a confirm listing the destructive statements. Backup "Restore" opens a confirm with the source/target DB names. The same primitive is reused. Each feature's existing #issue closes (this is the unifying fix).
Needs human verify
Yes (UX flow needs Tauri runtime).
Labels: audit, area/cross-cutting, severity/p1, kind/security