fix: 全面代码审查修复 — 安全/类型/持久化/代码质量 (28项)
🔴 CRITICAL (1): - tauri.conf.json: CSP 从 null 改为最小权限策略 🟠 HIGH (6): - 新建 capabilities/default.json: Tauri v2 权限约束(store/dialog/clipboard) - cli: 路径遍历防护 — 拒绝含 ParentDir 组件的输出路径 - HistoryList: 删除/清空同步持久化到 store,历史点击用 formData 回填 - ExportPanel: 移除 console.warn,getCurrentText any→QrState - useQrEncode: WiFi 密码在历史中脱敏显示(P:***),Store 实例缓存 🟡 MEDIUM (10): - mode.rs: Kanji fallback 从 UTF-8 字节改为 13-bit 零值占位(段内模式一致) - mode.rs: Shift JIS 第二字节跳过 0x7F 空洞,修正行内索引 - mode.rs: encode_numeric/alphanumeric 添加 debug_assert! 前置条件 - mask.rs: best_matrix.unwrap()→expect() 附错误信息 - version.rs: ec_info 仅返回 count>0 的 BlockInfo,EcInfo/BlockInfo 加 Debug+Clone - types/index.ts: HistoryEntry.mode string→ModeType,新增 formData 字段 - qrContext.tsx: 使用缓存 Store 加载历史 🟢 LOW (11): - cargo fmt 全部文件 - svg.rs: String::new()→with_capacity() 预分配 - patterns.rs: encode_format_info 拆分为两行提高可读性 - png.rs: 提取 fill_module() 辅助函数降低嵌套 - ErrorBoundary: 添加 componentDidCatch 错误日志入口 - QrPreview: dangerouslySetInnerHTML→<img>+data URL(安全),loading 状态指示 - galois.rs/version.rs: 5 处 #[allow(clippy::indexing_slicing)]+安全文档 - 新建 utils/qrText.ts: 集中管理 6 种模式的文本构造,消除 ExportPanel/mode 间重复 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useQrState } from '../store/qrContext';
|
||||
import { useQrEncode } from '../hooks/useQrEncode';
|
||||
import { buildEmailText } from '../utils/qrText';
|
||||
|
||||
export default function EmailMode() {
|
||||
const { state, dispatch } = useQrState();
|
||||
@@ -8,8 +9,7 @@ export default function EmailMode() {
|
||||
const update = (field: string, value: string) => {
|
||||
const data = { ...state.formData, [field]: value };
|
||||
dispatch({ type: 'SET_FORM_DATA', payload: data });
|
||||
const mailto = `mailto:${data.to || ''}?subject=${encodeURIComponent(data.subject || '')}&body=${encodeURIComponent(data.body || '')}`;
|
||||
encode(mailto);
|
||||
encode(buildEmailText(data));
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useQrState } from '../store/qrContext';
|
||||
import { useQrEncode } from '../hooks/useQrEncode';
|
||||
import { buildPhoneText } from '../utils/qrText';
|
||||
|
||||
export default function PhoneMode() {
|
||||
const { state, dispatch } = useQrState();
|
||||
@@ -7,7 +8,7 @@ export default function PhoneMode() {
|
||||
|
||||
const update = (number: string) => {
|
||||
dispatch({ type: 'SET_FORM_DATA', payload: { number } });
|
||||
encode(`tel:${number}`);
|
||||
encode(buildPhoneText({ number }));
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useQrState } from '../store/qrContext';
|
||||
import { useQrEncode } from '../hooks/useQrEncode';
|
||||
import { buildSmsText } from '../utils/qrText';
|
||||
|
||||
export default function SmsMode() {
|
||||
const { state, dispatch } = useQrState();
|
||||
@@ -8,7 +9,7 @@ export default function SmsMode() {
|
||||
const update = (field: string, value: string) => {
|
||||
const data = { ...state.formData, [field]: value };
|
||||
dispatch({ type: 'SET_FORM_DATA', payload: data });
|
||||
encode(`smsto:${data.number || ''}:${data.message || ''}`);
|
||||
encode(buildSmsText(data));
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useQrState } from '../store/qrContext';
|
||||
import { useQrEncode } from '../hooks/useQrEncode';
|
||||
import { buildVCardText } from '../utils/qrText';
|
||||
|
||||
const FIELDS = [
|
||||
{ key: 'name', placeholder: '姓名' },
|
||||
@@ -16,8 +17,7 @@ export default function VCardMode() {
|
||||
const update = (field: string, value: string) => {
|
||||
const data = { ...state.formData, [field]: value };
|
||||
dispatch({ type: 'SET_FORM_DATA', payload: data });
|
||||
const vcard = `BEGIN:VCARD\nVERSION:3.0\nFN:${data.name || ''}\nTEL:${data.phone || ''}\nEMAIL:${data.email || ''}\nORG:${data.company || ''}\nADR:${data.address || ''}\nEND:VCARD`;
|
||||
encode(vcard);
|
||||
encode(buildVCardText(data));
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
import { useQrState } from '../store/qrContext';
|
||||
import { useQrEncode } from '../hooks/useQrEncode';
|
||||
import { buildWifiText } from '../utils/qrText';
|
||||
|
||||
export default function WifiMode() {
|
||||
const { state, dispatch } = useQrState();
|
||||
const { encode } = useQrEncode();
|
||||
|
||||
const buildWifiText = (ssid: string, password: string, encryption: string, hidden: boolean) => {
|
||||
if (!ssid) return '';
|
||||
return `WIFI:T:${encryption};S:${ssid};P:${password};${hidden ? 'H:true;' : ''};`;
|
||||
};
|
||||
|
||||
const update = (field: string, value: string | boolean) => {
|
||||
const data = { ...state.formData, [field]: String(value) };
|
||||
/** checkbox 的 boolean 值统一转为 'true'/'false' 字符串存入 formData */
|
||||
const update = (field: string, value: string) => {
|
||||
const data = { ...state.formData, [field]: value };
|
||||
dispatch({ type: 'SET_FORM_DATA', payload: data });
|
||||
const wifiText = buildWifiText(
|
||||
data.ssid || '', data.password || '', data.encryption || 'WPA', data.hidden === 'true'
|
||||
);
|
||||
encode(wifiText);
|
||||
encode(buildWifiText(data));
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -36,7 +30,7 @@ export default function WifiMode() {
|
||||
</select>
|
||||
<label className="flex items-center gap-1 text-sm text-gray-500 dark:text-gray-400 whitespace-nowrap">
|
||||
<input type="checkbox" checked={state.formData.hidden === 'true'}
|
||||
onChange={e => update('hidden', e.target.checked)} />
|
||||
onChange={e => update('hidden', e.target.checked ? 'true' : 'false')} />
|
||||
隐藏
|
||||
</label>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user