From a6b1255dc0f02e282f74c787224b9485ce6e6047 Mon Sep 17 00:00:00 2001 From: iven Date: Sun, 15 Mar 2026 19:22:51 +0800 Subject: [PATCH] security(phase-9): complete security hardening - Add safeJsonParse utility with schema validation - Migrate tokens to OS keyring storage - Add Ed25519 key encryption at rest - Enable WSS configuration option - Fix JSON.parse in HandParamsForm, WorkflowEditor, WorkflowList - Update test mock data to match valid status values Co-Authored-By: Claude Opus 4.6 --- desktop/src/components/HandParamsForm.tsx | 21 +-- desktop/src/components/RightPanel.tsx | 2 + desktop/src/components/WorkflowEditor.tsx | 15 +- desktop/src/components/WorkflowList.tsx | 9 +- desktop/src/lib/gateway-client.ts | 123 +++++++++----- desktop/src/lib/json-utils.ts | 173 +++++++++++++++++++ desktop/src/lib/secure-storage.ts | 198 ++++++++++++++++++++++ desktop/src/store/teamStore.ts | 3 +- docs/SYSTEM_ANALYSIS.md | 17 +- tests/desktop/gatewayStore.test.ts | 12 +- 10 files changed, 499 insertions(+), 74 deletions(-) create mode 100644 desktop/src/lib/json-utils.ts diff --git a/desktop/src/components/HandParamsForm.tsx b/desktop/src/components/HandParamsForm.tsx index 796f1a5..e080999 100644 --- a/desktop/src/components/HandParamsForm.tsx +++ b/desktop/src/components/HandParamsForm.tsx @@ -31,6 +31,7 @@ import { Info, } from 'lucide-react'; import type { HandParameter } from '../types/hands'; +import { parseJsonOrDefault, safeJsonParse } from '../lib/json-utils'; // === Types === @@ -144,13 +145,9 @@ function getPresetStorageKey(handId: string): string { } function loadPresets(handId: string): ParameterPreset[] { - try { - const stored = localStorage.getItem(getPresetStorageKey(handId)); - if (stored) { - return JSON.parse(stored) as ParameterPreset[]; - } - } catch { - // Ignore parse errors + const stored = localStorage.getItem(getPresetStorageKey(handId)); + if (stored) { + return parseJsonOrDefault(stored, []); } return []; } @@ -405,15 +402,15 @@ function ObjectParamInput({ param, value, onChange, disabled, error }: ParamInpu return; } - try { - const parsed = JSON.parse(text); - if (typeof parsed === 'object' && !Array.isArray(parsed)) { - onChange(parsed); + const result = safeJsonParse(text); + if (result.success) { + if (typeof result.data === 'object' && !Array.isArray(result.data)) { + onChange(result.data as Record); setParseError(null); } else { setParseError('Value must be a JSON object'); } - } catch { + } else { setParseError('Invalid JSON format'); } }; diff --git a/desktop/src/components/RightPanel.tsx b/desktop/src/components/RightPanel.tsx index aaefbb9..225608a 100644 --- a/desktop/src/components/RightPanel.tsx +++ b/desktop/src/components/RightPanel.tsx @@ -574,6 +574,8 @@ export function RightPanel() { + + )} ); diff --git a/desktop/src/components/WorkflowEditor.tsx b/desktop/src/components/WorkflowEditor.tsx index 9c3f537..4a8fff2 100644 --- a/desktop/src/components/WorkflowEditor.tsx +++ b/desktop/src/components/WorkflowEditor.tsx @@ -23,6 +23,7 @@ import { AlertCircle, GitBranch, } from 'lucide-react'; +import { safeJsonParse } from '../lib/json-utils'; // === Types === @@ -163,11 +164,15 @@ function StepEditor({ step, hands, index, onUpdate, onRemove, onMoveUp, onMoveDo