mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-21 12:51:47 +08:00
🐛 fix(security): 修复 macOS 无法打开应用及三平台依赖系统钥匙串的问题
- 密文存储:新增 dailysecret 本地存储引擎,连接/代理/AI 密钥不再依赖系统钥匙串 - 启动迁移:自动将已有钥匙串密文迁移到本地 JSON,用户无感知 - WebKit 迁移:从旧版 Wails WebKit LocalStorage 中恢复连接与代理数据 - DMG 修复:移除 --sandbox-safe 避免扩展属性污染签名,新增 xattr 清理与签名校验 - 安全适配:钥匙串不可用时标记完成而非回滚,消除无钥匙串环境下的阻塞 - 出口脱敏:所有连接/代理 API 返回前统一 sanitize 防止密文泄漏
This commit is contained in:
@@ -22,6 +22,7 @@ import { blurToFilter, normalizeBlurForPlatform, normalizeOpacityForPlatform, is
|
||||
import { DATA_GRID_COLUMN_WIDTH_MODE_OPTIONS, sanitizeDataTableColumnWidthMode } from './utils/dataGridDisplay';
|
||||
import { getMacNativeTitlebarPaddingLeft, getMacNativeTitlebarPaddingRight, shouldHandleMacNativeFullscreenShortcut, shouldSuppressMacNativeEscapeExit } from './utils/macWindow';
|
||||
import { shouldEnableMacWindowDiagnostics } from './utils/macWindowDiagnostics';
|
||||
import { resolveAboutDisplayVersion } from './utils/appVersionDisplay';
|
||||
import { buildOverlayWorkbenchTheme } from './utils/overlayWorkbenchTheme';
|
||||
import { getConnectionWorkbenchState } from './utils/startupReadiness';
|
||||
import { toSaveGlobalProxyInput } from './utils/globalProxyDraft';
|
||||
@@ -181,6 +182,7 @@ function App() {
|
||||
const effectiveBlur = normalizeBlurForPlatform(resolvedAppearance.blur);
|
||||
const blurFilter = blurToFilter(effectiveBlur);
|
||||
const [runtimePlatform, setRuntimePlatform] = useState('');
|
||||
const [runtimeBuildType, setRuntimeBuildType] = useState('');
|
||||
const [isLinuxRuntime, setIsLinuxRuntime] = useState(false);
|
||||
const [isStoreHydrated, setIsStoreHydrated] = useState(() => useStore.persist.hasHydrated());
|
||||
const [hasLoadedSecureConfig, setHasLoadedSecureConfig] = useState(false);
|
||||
@@ -219,13 +221,16 @@ function App() {
|
||||
|
||||
const windowCornerRadius = 14;
|
||||
useEffect(()=>{
|
||||
if (typeof document === 'undefined' || !document.body) {
|
||||
return;
|
||||
}
|
||||
switch(windowState){
|
||||
case 'fullscreen':
|
||||
case 'maximized':
|
||||
document.body.setAttribute('--gonavi-border-radius', '0px');
|
||||
document.body.style.setProperty('--gonavi-border-radius', '0px');
|
||||
break;
|
||||
default:
|
||||
document.body.setAttribute('--gonavi-border-radius', `${windowCornerRadius}px`);
|
||||
document.body.style.setProperty('--gonavi-border-radius', `${windowCornerRadius}px`);
|
||||
break;
|
||||
}
|
||||
}, [windowState]);
|
||||
@@ -246,6 +251,7 @@ function App() {
|
||||
if (cancelled) return;
|
||||
const platform = String(env?.platform || '').toLowerCase();
|
||||
setRuntimePlatform(platform);
|
||||
setRuntimeBuildType(String(env?.buildType || '').toLowerCase());
|
||||
setIsLinuxRuntime(platform === 'linux');
|
||||
})
|
||||
.catch(() => {
|
||||
@@ -1089,6 +1095,7 @@ function App() {
|
||||
const isAboutOpenRef = React.useRef(false);
|
||||
const [aboutLoading, setAboutLoading] = useState(false);
|
||||
const [aboutInfo, setAboutInfo] = useState<{ version: string; author: string; buildTime?: string; repoUrl?: string; issueUrl?: string; releaseUrl?: string; communityUrl?: string } | null>(null);
|
||||
const aboutDisplayVersion = resolveAboutDisplayVersion(runtimeBuildType, aboutInfo?.version);
|
||||
const [aboutUpdateStatus, setAboutUpdateStatus] = useState<string>('');
|
||||
const [lastUpdateInfo, setLastUpdateInfo] = useState<UpdateInfo | null>(null);
|
||||
const [updateDownloadProgress, setUpdateDownloadProgress] = useState<{
|
||||
@@ -1145,7 +1152,11 @@ function App() {
|
||||
const isWindowsRuntime = runtimePlatform === 'windows'
|
||||
|| (runtimePlatform === '' && isWindowsPlatform());
|
||||
const useNativeMacWindowControls = isMacRuntime && appearance.useNativeMacWindowControls === true;
|
||||
const macWindowDiagnosticsEnabled = shouldEnableMacWindowDiagnostics(isMacRuntime, import.meta.env.DEV);
|
||||
const macWindowDiagnosticsEnabled = shouldEnableMacWindowDiagnostics(
|
||||
isMacRuntime,
|
||||
import.meta.env.DEV,
|
||||
import.meta.env.VITE_GONAVI_ENABLE_MAC_WINDOW_DIAGNOSTICS,
|
||||
);
|
||||
|
||||
const emitWindowDiagnostic = useCallback(async (stage: string, extra: Record<string, unknown> = {}) => {
|
||||
if (!macWindowDiagnosticsEnabled) {
|
||||
@@ -3040,7 +3051,7 @@ function App() {
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: 12 }}>
|
||||
<div>
|
||||
<div style={{ marginBottom: 6, fontWeight: 600 }}>版本</div>
|
||||
<div style={utilityMutedTextStyle}>{aboutInfo?.version || '未知'}</div>
|
||||
<div style={utilityMutedTextStyle}>{aboutDisplayVersion}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ marginBottom: 6, fontWeight: 600 }}>作者</div>
|
||||
|
||||
21
frontend/src/utils/appVersionDisplay.test.ts
Normal file
21
frontend/src/utils/appVersionDisplay.test.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { resolveAboutDisplayVersion } from './appVersionDisplay';
|
||||
|
||||
describe('resolveAboutDisplayVersion', () => {
|
||||
it('shows fixed dev version for development build', () => {
|
||||
expect(resolveAboutDisplayVersion('development', '0.6.5')).toBe('0.0.1-dev');
|
||||
});
|
||||
|
||||
it('shows fixed dev version for wails dev build type', () => {
|
||||
expect(resolveAboutDisplayVersion('dev', '0.6.5')).toBe('0.0.1-dev');
|
||||
});
|
||||
|
||||
it('keeps real version for non-development builds', () => {
|
||||
expect(resolveAboutDisplayVersion('production', '0.6.5')).toBe('0.6.5');
|
||||
});
|
||||
|
||||
it('falls back to unknown when version is empty outside development', () => {
|
||||
expect(resolveAboutDisplayVersion('production', '')).toBe('未知');
|
||||
});
|
||||
});
|
||||
14
frontend/src/utils/appVersionDisplay.ts
Normal file
14
frontend/src/utils/appVersionDisplay.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
const DEV_ABOUT_VERSION = '0.0.1-dev';
|
||||
|
||||
export const resolveAboutDisplayVersion = (
|
||||
buildType: string,
|
||||
version: string | undefined,
|
||||
): string => {
|
||||
const normalizedBuildType = String(buildType || '').trim().toLowerCase();
|
||||
if (normalizedBuildType === 'development' || normalizedBuildType === 'dev') {
|
||||
return DEV_ABOUT_VERSION;
|
||||
}
|
||||
|
||||
const normalizedVersion = String(version || '').trim();
|
||||
return normalizedVersion || '未知';
|
||||
};
|
||||
@@ -4,14 +4,22 @@ import { shouldEnableMacWindowDiagnostics } from './macWindowDiagnostics';
|
||||
|
||||
describe('macWindowDiagnostics', () => {
|
||||
it('stays disabled outside macOS runtime', () => {
|
||||
expect(shouldEnableMacWindowDiagnostics(false, true)).toBe(false);
|
||||
expect(shouldEnableMacWindowDiagnostics(false, true, 'true')).toBe(false);
|
||||
});
|
||||
|
||||
it('stays disabled for production builds on macOS', () => {
|
||||
expect(shouldEnableMacWindowDiagnostics(true, false)).toBe(false);
|
||||
expect(shouldEnableMacWindowDiagnostics(true, false, 'true')).toBe(false);
|
||||
});
|
||||
|
||||
it('enables diagnostics only for macOS development builds', () => {
|
||||
expect(shouldEnableMacWindowDiagnostics(true, true)).toBe(true);
|
||||
it('stays disabled by default for macOS development builds', () => {
|
||||
expect(shouldEnableMacWindowDiagnostics(true, true)).toBe(false);
|
||||
expect(shouldEnableMacWindowDiagnostics(true, true, '')).toBe(false);
|
||||
expect(shouldEnableMacWindowDiagnostics(true, true, '0')).toBe(false);
|
||||
});
|
||||
|
||||
it('enables diagnostics only when explicitly opted in on macOS development builds', () => {
|
||||
expect(shouldEnableMacWindowDiagnostics(true, true, '1')).toBe(true);
|
||||
expect(shouldEnableMacWindowDiagnostics(true, true, 'true')).toBe(true);
|
||||
expect(shouldEnableMacWindowDiagnostics(true, true, 'yes')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,22 @@
|
||||
const isTruthyFlag = (value: string | undefined): boolean => {
|
||||
switch (String(value || '').trim().toLowerCase()) {
|
||||
case '1':
|
||||
case 'true':
|
||||
case 'yes':
|
||||
case 'on':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const shouldEnableMacWindowDiagnostics = (
|
||||
isMacRuntime: boolean,
|
||||
isDevBuild: boolean,
|
||||
envValue?: string,
|
||||
): boolean => {
|
||||
return isMacRuntime && isDevBuild;
|
||||
if (!isMacRuntime || !isDevBuild) {
|
||||
return false;
|
||||
}
|
||||
return isTruthyFlag(envValue);
|
||||
};
|
||||
|
||||
7
frontend/src/vite-env.d.ts
vendored
7
frontend/src/vite-env.d.ts
vendored
@@ -1,2 +1,9 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_GONAVI_ENABLE_MAC_WINDOW_DIAGNOSTICS?: string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user