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 <noreply@anthropic.com>
This commit is contained in:
iven
2026-03-15 19:22:51 +08:00
parent e3d164e9d2
commit a6b1255dc0
10 changed files with 499 additions and 74 deletions

View File

@@ -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<ParameterPreset[]>(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<unknown>(text);
if (result.success) {
if (typeof result.data === 'object' && !Array.isArray(result.data)) {
onChange(result.data as Record<string, unknown>);
setParseError(null);
} else {
setParseError('Value must be a JSON object');
}
} catch {
} else {
setParseError('Invalid JSON format');
}
};