From f0319b7deb6b0a9c13c3f946dc8debfe18f8ab2c Mon Sep 17 00:00:00 2001 From: lincube Date: Mon, 11 May 2026 18:06:36 +0800 Subject: [PATCH] Add preview controls and settings UI tweaks Introduce GridPreviewControl and CornerRadiusPreviewControl for visual previews and wire them into the Components settings (add ScreenAspectRatio, CornerRadiusPreviewValue, and screen aspect init). Refactor ComponentsSettingsPage UI to show live previews. Improve DataSettingsPage layout and storage bar logic (use item percentages directly, include remaining segment, adjust visuals and visibility triggers). Simplify LauncherSettingsPage header/appearance layout. Add SECURITY_AUDIT_REPORT.md, analysis summary, mockup HTML, and a local .claude settings file. --- .claude/settings.local.json | 8 + .../Controls/CornerRadiusPreviewControl.cs | 101 ++ .../Controls/GridPreviewControl.cs | 159 ++++ .../ViewModels/SettingsViewModels.cs | 9 + .../ComponentsSettingsPage.axaml | 28 +- .../ComponentsSettingsPage.axaml.cs | 23 + .../SettingsPages/DataSettingsPage.axaml | 113 ++- .../SettingsPages/DataSettingsPage.axaml.cs | 31 +- .../SettingsPages/LauncherSettingsPage.axaml | 66 +- SECURITY_AUDIT_REPORT.md | 196 ++++ docs/auto_commit_md/20260511_SUMMARY.md | 57 ++ mockup-noise-level.html | 898 ++++++++++++++++++ 12 files changed, 1560 insertions(+), 129 deletions(-) create mode 100644 .claude/settings.local.json create mode 100644 LanMountainDesktop/Controls/CornerRadiusPreviewControl.cs create mode 100644 LanMountainDesktop/Controls/GridPreviewControl.cs create mode 100644 SECURITY_AUDIT_REPORT.md create mode 100644 docs/auto_commit_md/20260511_SUMMARY.md create mode 100644 mockup-noise-level.html diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..d7300de --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,8 @@ +{ + "permissions": { + "allow": [ + "Bash(ls -la \"/d/github/LanMountainDesktop/.claude/worktrees/agent-a4c5412322421ab67\" && ls -la \"/d/github/LanMountainDesktop\" && ls -la \"/d/github\")", + "Read(//d/github/**)" + ] + } +} diff --git a/LanMountainDesktop/Controls/CornerRadiusPreviewControl.cs b/LanMountainDesktop/Controls/CornerRadiusPreviewControl.cs new file mode 100644 index 0000000..bcf8a85 --- /dev/null +++ b/LanMountainDesktop/Controls/CornerRadiusPreviewControl.cs @@ -0,0 +1,101 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; + +namespace LanMountainDesktop.Controls; + +public class CornerRadiusPreviewControl : Control +{ + public static readonly StyledProperty RadiusProperty = + AvaloniaProperty.Register(nameof(Radius), 24); + + public static readonly StyledProperty ShapeBrushProperty = + AvaloniaProperty.Register(nameof(ShapeBrush)); + + public static readonly StyledProperty GuideBrushProperty = + AvaloniaProperty.Register(nameof(GuideBrush)); + + public static readonly StyledProperty FillBrushProperty = + AvaloniaProperty.Register(nameof(FillBrush)); + + public double Radius + { + get => GetValue(RadiusProperty); + set => SetValue(RadiusProperty, value); + } + + public IBrush? ShapeBrush + { + get => GetValue(ShapeBrushProperty); + set => SetValue(ShapeBrushProperty, value); + } + + public IBrush? GuideBrush + { + get => GetValue(GuideBrushProperty); + set => SetValue(GuideBrushProperty, value); + } + + public IBrush? FillBrush + { + get => GetValue(FillBrushProperty); + set => SetValue(FillBrushProperty, value); + } + + static CornerRadiusPreviewControl() + { + AffectsRender( + RadiusProperty, + ShapeBrushProperty, + GuideBrushProperty, + FillBrushProperty); + } + + public override void Render(DrawingContext context) + { + var w = Bounds.Width; + var h = Bounds.Height; + if (w <= 0 || h <= 0) return; + + var shapeBrush = ShapeBrush ?? Brushes.Gray; + var guideBrush = GuideBrush ?? Brushes.Gray; + var fillBrush = FillBrush ?? new SolidColorBrush(Colors.Gray, 0.08); + + var padding = 24.0; + var maxShapeW = w - padding * 2; + var maxShapeH = h - padding * 2; + var shapeSize = Math.Min(maxShapeW, maxShapeH); + + if (shapeSize < 20) return; + + var ox = (w - shapeSize) / 2; + var oy = (h - shapeSize) / 2; + + var r = Math.Min(Radius, shapeSize * 0.45); + r = Math.Max(0, r); + + var shapeRect = new Rect(ox, oy, shapeSize, shapeSize); + var shapePen = new Pen(shapeBrush, 1.5); + var dashPen = new Pen(guideBrush, 0.75, new DashStyle([4, 3], 0)); + + context.DrawRectangle(fillBrush, shapePen, shapeRect, r, r); + + if (r > 4) + { + var arcCenterX = ox + r; + var arcCenterY = oy + r; + + context.DrawLine( + dashPen, + new Point(arcCenterX, oy + r * 0.2), + new Point(arcCenterX, oy + r * 0.9)); + + context.DrawLine( + dashPen, + new Point(ox + r * 0.2, arcCenterY), + new Point(ox + r * 0.9, arcCenterY)); + + context.DrawEllipse(null, new Pen(guideBrush, 0.75), new Point(arcCenterX, arcCenterY), 2, 2); + } + } +} diff --git a/LanMountainDesktop/Controls/GridPreviewControl.cs b/LanMountainDesktop/Controls/GridPreviewControl.cs new file mode 100644 index 0000000..00ca334 --- /dev/null +++ b/LanMountainDesktop/Controls/GridPreviewControl.cs @@ -0,0 +1,159 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; + +namespace LanMountainDesktop.Controls; + +public class GridPreviewControl : Control +{ + public static readonly StyledProperty CellsProperty = + AvaloniaProperty.Register(nameof(Cells), 12); + + public static readonly StyledProperty AspectRatioProperty = + AvaloniaProperty.Register(nameof(AspectRatio), 16.0 / 9.0); + + public static readonly StyledProperty EdgeInsetPercentProperty = + AvaloniaProperty.Register(nameof(EdgeInsetPercent), 0); + + public static readonly StyledProperty GridBrushProperty = + AvaloniaProperty.Register(nameof(GridBrush)); + + public static readonly StyledProperty ScreenBorderBrushProperty = + AvaloniaProperty.Register(nameof(ScreenBorderBrush)); + + public static readonly StyledProperty InsetBrushProperty = + AvaloniaProperty.Register(nameof(InsetBrush)); + + public int Cells + { + get => GetValue(CellsProperty); + set => SetValue(CellsProperty, value); + } + + public double AspectRatio + { + get => GetValue(AspectRatioProperty); + set => SetValue(AspectRatioProperty, value); + } + + public int EdgeInsetPercent + { + get => GetValue(EdgeInsetPercentProperty); + set => SetValue(EdgeInsetPercentProperty, value); + } + + public IBrush? GridBrush + { + get => GetValue(GridBrushProperty); + set => SetValue(GridBrushProperty, value); + } + + public IBrush? ScreenBorderBrush + { + get => GetValue(ScreenBorderBrushProperty); + set => SetValue(ScreenBorderBrushProperty, value); + } + + public IBrush? InsetBrush + { + get => GetValue(InsetBrushProperty); + set => SetValue(InsetBrushProperty, value); + } + + static GridPreviewControl() + { + AffectsRender( + CellsProperty, + AspectRatioProperty, + EdgeInsetPercentProperty, + GridBrushProperty, + ScreenBorderBrushProperty, + InsetBrushProperty); + } + + public override void Render(DrawingContext context) + { + var w = Bounds.Width; + var h = Bounds.Height; + if (w <= 0 || h <= 0) return; + + var ratio = AspectRatio > 0 ? AspectRatio : 16.0 / 9.0; + double screenW, screenH; + if (w / h > ratio) + { + screenH = h; + screenW = h * ratio; + } + else + { + screenW = w; + screenH = w / ratio; + } + + var offsetX = (w - screenW) / 2; + var offsetY = (h - screenH) / 2; + + var borderBrush = ScreenBorderBrush ?? Brushes.Gray; + var gridBrush = GridBrush ?? Brushes.Gray; + var insetBrush = InsetBrush ?? new SolidColorBrush(Colors.Gray, 0.12); + + var borderThickness = 1.5; + var borderPen = new Pen(borderBrush, borderThickness); + + var cells = Math.Max(1, Cells); + var cellSize = screenW / cells; + var rows = (int)Math.Floor(screenH / cellSize); + + var insetPercent = Math.Clamp(EdgeInsetPercent, 0, 30); + var insetRatio = insetPercent / 100d; + var insetPx = Math.Min(cellSize * insetRatio, screenH * 0.15); + + var contentX = offsetX + insetPx; + var contentY = offsetY + insetPx; + var contentW = Math.Max(1, screenW - insetPx * 2); + var contentH = Math.Max(1, screenH - insetPx * 2); + + var screenRect = new Rect(offsetX, offsetY, screenW, screenH); + var contentRect = new Rect(contentX, contentY, contentW, contentH); + + var cornerRadius = Math.Min(6, Math.Min(screenW, screenH) * 0.03); + + using (context.PushClip(screenRect)) + { + if (insetPx > 0.5) + { + context.DrawRectangle(insetBrush, null, screenRect, cornerRadius, cornerRadius); + context.DrawRectangle( + new SolidColorBrush(Colors.Transparent), + new Pen(borderBrush, 0.75, new DashStyle([3, 3], 0)), + contentRect, + cornerRadius * 0.6, + cornerRadius * 0.6); + } + + var dashSegment = Math.Max(2, cellSize * 0.25); + var dashPen = new Pen(gridBrush, 1.0, new DashStyle([dashSegment, dashSegment], 0)); + + for (var col = 1; col < cells; col++) + { + var x = contentX + col * (contentW / cells); + if (x < contentX + contentW) + { + context.DrawLine(dashPen, new Point(x, contentY), new Point(x, contentY + contentH)); + } + } + + for (var row = 1; row < rows; row++) + { + var y = contentY + row * (contentH / rows); + if (y < contentY + contentH) + { + context.DrawLine(dashPen, new Point(contentX, y), new Point(contentX + contentW, y)); + } + } + } + + var adjustedRect = screenRect.Deflate(borderThickness / 2); + context.DrawRectangle(null, borderPen, adjustedRect, cornerRadius, cornerRadius); + } +} diff --git a/LanMountainDesktop/ViewModels/SettingsViewModels.cs b/LanMountainDesktop/ViewModels/SettingsViewModels.cs index 6cfc3bc..fd014ca 100644 --- a/LanMountainDesktop/ViewModels/SettingsViewModels.cs +++ b/LanMountainDesktop/ViewModels/SettingsViewModels.cs @@ -16,6 +16,7 @@ using LanMountainDesktop.Models; using LanMountainDesktop.PluginSdk; using LanMountainDesktop.Services; using LanMountainDesktop.Services.Settings; +using LanMountainDesktop.Appearance; using LanMountainDesktop.Settings.Core; using LanMountainDesktop.Shared.Contracts.Launcher; @@ -887,6 +888,9 @@ public sealed partial class ComponentsSettingsPageViewModel : ViewModelBase [ObservableProperty] private int _shortSideCells; + [ObservableProperty] + private double _screenAspectRatio = 16.0 / 9.0; + [ObservableProperty] private int _edgeInsetPercent; @@ -914,6 +918,9 @@ public sealed partial class ComponentsSettingsPageViewModel : ViewModelBase [ObservableProperty] private string _cornerRadiusStyle = GlobalAppearanceSettings.DefaultCornerRadiusStyle; + [ObservableProperty] + private double _cornerRadiusPreviewValue = 24; + [ObservableProperty] private IReadOnlyList _cornerRadiusStyleOptions = []; @@ -947,6 +954,7 @@ public sealed partial class ComponentsSettingsPageViewModel : ViewModelBase SelectedCornerRadiusStyle = CornerRadiusStyleOptions.FirstOrDefault(option => string.Equals(option.Value, CornerRadiusStyle, StringComparison.OrdinalIgnoreCase)) ?? CornerRadiusStyleOptions.FirstOrDefault(o => o.Value == GlobalAppearanceSettings.DefaultCornerRadiusStyle); + CornerRadiusPreviewValue = AppearanceCornerRadiusTokenFactory.Create(CornerRadiusStyle).Component.TopLeft; } partial void OnShortSideCellsChanged(int value) @@ -987,6 +995,7 @@ public sealed partial class ComponentsSettingsPageViewModel : ViewModelBase } CornerRadiusStyle = value.Value; + CornerRadiusPreviewValue = AppearanceCornerRadiusTokenFactory.Create(value.Value).Component.TopLeft; SaveComponentCornerRadius(); } diff --git a/LanMountainDesktop/Views/SettingsPages/ComponentsSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/ComponentsSettingsPage.axaml index 788beb4..3c4c6e2 100644 --- a/LanMountainDesktop/Views/SettingsPages/ComponentsSettingsPage.axaml +++ b/LanMountainDesktop/Views/SettingsPages/ComponentsSettingsPage.axaml @@ -1,4 +1,4 @@ - + + + + + + + + diff --git a/LanMountainDesktop/Views/SettingsPages/ComponentsSettingsPage.axaml.cs b/LanMountainDesktop/Views/SettingsPages/ComponentsSettingsPage.axaml.cs index f553ed1..b5a344e 100644 --- a/LanMountainDesktop/Views/SettingsPages/ComponentsSettingsPage.axaml.cs +++ b/LanMountainDesktop/Views/SettingsPages/ComponentsSettingsPage.axaml.cs @@ -1,3 +1,6 @@ +using System.Linq; +using Avalonia.Controls; +using Avalonia.Platform; using LanMountainDesktop.PluginSdk; using LanMountainDesktop.Services.Settings; using LanMountainDesktop.ViewModels; @@ -24,6 +27,26 @@ public partial class ComponentsSettingsPage : SettingsPageBase ViewModel = viewModel; DataContext = ViewModel; InitializeComponent(); + InitScreenAspectRatio(); + } + + private void InitScreenAspectRatio() + { + try + { + var topLevel = TopLevel.GetTopLevel(this); + if (topLevel is null) return; + + var screen = topLevel.Screens.Primary ?? topLevel.Screens.All.FirstOrDefault(); + if (screen is not null && screen.Bounds.Height > 0) + { + ViewModel.ScreenAspectRatio = (double)screen.Bounds.Width / screen.Bounds.Height; + } + } + catch + { + // 无法获取屏幕信息时保持默认 16:9 + } } public ComponentsSettingsPageViewModel ViewModel { get; } diff --git a/LanMountainDesktop/Views/SettingsPages/DataSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/DataSettingsPage.axaml index c6b6d51..7b083a4 100644 --- a/LanMountainDesktop/Views/SettingsPages/DataSettingsPage.axaml +++ b/LanMountainDesktop/Views/SettingsPages/DataSettingsPage.axaml @@ -28,77 +28,74 @@ - - - - - - - - - - - + + + + Opacity="0.65" /> + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/LanMountainDesktop/Views/SettingsPages/DataSettingsPage.axaml.cs b/LanMountainDesktop/Views/SettingsPages/DataSettingsPage.axaml.cs index f8c6cbb..a73c4e8 100644 --- a/LanMountainDesktop/Views/SettingsPages/DataSettingsPage.axaml.cs +++ b/LanMountainDesktop/Views/SettingsPages/DataSettingsPage.axaml.cs @@ -55,7 +55,8 @@ public partial class DataSettingsPage : SettingsPageBase return; } - if (string.Equals(e.PropertyName, nameof(DataSettingsPageViewModel.HasData), StringComparison.Ordinal)) + if (string.Equals(e.PropertyName, nameof(DataSettingsPageViewModel.HasData), StringComparison.Ordinal) || + string.Equals(e.PropertyName, nameof(DataSettingsPageViewModel.DiskUsagePercentage), StringComparison.Ordinal)) { RebuildStorageBar(); } @@ -80,43 +81,31 @@ public partial class DataSettingsPage : SettingsPageBase StorageBarGrid.ColumnDefinitions.Clear(); StorageBarGrid.Children.Clear(); - if (!ViewModel.HasData) - { - return; - } - var visibleItems = ViewModel.Items .Where(item => item.Percentage > 0) .OrderByDescending(item => item.Percentage) .ToList(); - if (visibleItems.Count == 0) - { - return; - } - - var totalPercent = visibleItems.Sum(item => item.Percentage); - if (totalPercent <= 0) - { - return; - } - var idx = 0; foreach (var item in visibleItems) { - var normalized = Math.Max(0.1, item.Percentage / totalPercent * 100d); - StorageBarGrid.ColumnDefinitions.Add(new ColumnDefinition(new GridLength(normalized, GridUnitType.Star))); - + var width = Math.Max(0.1, item.Percentage); + StorageBarGrid.ColumnDefinitions.Add(new ColumnDefinition(new GridLength(width, GridUnitType.Star))); var segment = new Border { Background = ParseBrush(item.ColorHex), HorizontalAlignment = HorizontalAlignment.Stretch, VerticalAlignment = VerticalAlignment.Stretch }; - Grid.SetColumn(segment, idx++); StorageBarGrid.Children.Add(segment); } + + var remaining = 100d - ViewModel.DiskUsagePercentage; + if (remaining > 0) + { + StorageBarGrid.ColumnDefinitions.Add(new ColumnDefinition(new GridLength(remaining, GridUnitType.Star))); + } } private IBrush ParseBrush(string? hex) diff --git a/LanMountainDesktop/Views/SettingsPages/LauncherSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/LauncherSettingsPage.axaml index c0a2455..8ee66d9 100644 --- a/LanMountainDesktop/Views/SettingsPages/LauncherSettingsPage.axaml +++ b/LanMountainDesktop/Views/SettingsPages/LauncherSettingsPage.axaml @@ -1,58 +1,21 @@ - - - - - - + + - - - - - - - - - - - - - - @@ -71,11 +34,16 @@ - + + + + + diff --git a/SECURITY_AUDIT_REPORT.md b/SECURITY_AUDIT_REPORT.md new file mode 100644 index 0000000..a6ab7bd --- /dev/null +++ b/SECURITY_AUDIT_REPORT.md @@ -0,0 +1,196 @@ +# 安全审计报告 + +**项目**: LanMountainDesktop +**审计日期**: 2026-05-11 +**审计范围**: 整体代码库安全性评估 +**审计方法**: 自动化静态代码分析 + 架构审查 + +--- + +## 执行摘要 + +本次审计对 LanMountainDesktop 代码库进行了系统性安全评估,重点关注认证与访问控制、注入向量、外部交互以及敏感数据处理等高风险攻击面。 + +**审计结论**: 发现 **4 个已确认的中等及以上严重度漏洞**,建议立即修复。 + +--- + +## 已确认漏洞 + +### 漏洞 #1 - PostHog API Key 硬编码(高严重度) + +| 属性 | 详情 | +|------|------| +| **严重度** | 高 | +| **CWE** | CWE-798 - 使用硬编码凭证 | +| **位置** | `LanMountainDesktop/Services/PostHogUsageTelemetryService.cs:14` | +| **攻击者画像** | 源代码仓库的任何访问者(包括外部攻击者通过代码泄露或供应链攻击) | +| **可控输入** | 无(静态硬编码密钥) | + +**代码路径**: +```csharp +// PostHogUsageTelemetryService.cs:14 +private const string PostHogApiKey = "phc_bhQZvKDDfsEdLT6kkRFvrWMT8Pc5aCGGsnxoc5ijSf9"; +``` + +**影响**: +- 攻击者可能滥用此 API Key 向 PostHog 项目发送伪造遥测数据 +- 可能导致遥测数据污染或服务滥用 +- API Key 暴露在公开仓库中,任何人都能获取 + +**修复建议**: +```csharp +private const string PostHogApiKey = Environment.GetEnvironmentVariable("POSTHOG_API_KEY") + ?? throw new InvalidOperationException("PostHog API key not configured."); +``` + +--- + +### 漏洞 #2 - Sentry DSN 硬编码(高严重度) + +| 属性 | 详情 | +|------|------| +| **严重度** | 高 | +| **CWE** | CWE-798 - 使用硬编码凭证 | +| **位置** | `LanMountainDesktop/Services/SentryCrashTelemetryService.cs:15` | +| **攻击者画像** | 源代码仓库的任何访问者 | +| **可控输入** | 无(静态硬编码密钥) | + +**代码路径**: +```csharp +// SentryCrashTelemetryService.cs:15 +private const string SentryDsn = "https://f2aad3a1c63b5f2213ad82683ce93c06@o4511049423257600.ingest.us.sentry.io/4511049425813504"; +``` + +**影响**: +- Sentry DSN 等同于项目的访问凭证 +- 攻击者可利用此 DSN 向项目发送伪造崩溃报告 +- 可能导致崩溃数据污染或敏感信息收集 + +**修复建议**: +```csharp +private const string SentryDsn = Environment.GetEnvironmentVariable("SENTRY_DSN") + ?? throw new InvalidOperationException("Sentry DSN not configured."); +``` + +--- + +### 漏洞 #3 - 小米天气 API 签名密钥硬编码(高严重度) + +| 属性 | 详情 | +|------|------| +| **严重度** | 高 | +| **CWE** | CWE-798 - 使用硬编码凭证 | +| **位置** | `LanMountainDesktop/Services/XiaomiWeatherService.cs:25` | +| **攻击者画像** | 源代码仓库的任何访问者 | +| **可控输入** | 无(静态硬编码密钥) | + +**代码路径**: +```csharp +// XiaomiWeatherService.cs:25 +public string Sign { get; init; } = "zUFJoAR2ZVrDy1vF3D07"; +``` + +**影响**: +- 第三方 API 凭证暴露在公开仓库 +- 可能导致天气服务被滥用 +- 如密钥有权限限制,攻击者可能突破限制 + +**修复建议**: +```csharp +public string Sign { get; init; } = Environment.GetEnvironmentVariable("XIAOMI_WEATHER_SIGN") ?? ""; +``` + +--- + +### 漏洞 #4 - Sentry PII 收集配置(中等严重度) + +| 属性 | 详情 | +|------|------| +| **严重度** | 中等 | +| **CWE** | CWE-359 - 个人身份信息(PII)意外暴露 | +| **位置** | `LanMountainDesktop/Services/SentryCrashTelemetryService.cs:212` | +| **攻击者画像** | Sentry 后端管理员、内部威胁或数据泄露事件 | +| **可控输入** | 用户环境的机器名、用户名等系统信息 | +| **利用路径** | `程序启动 → TelemetryIdentityService.Initialize()` → 遥测数据上报 | + +**代码路径**: +```csharp +// SentryCrashTelemetryService.cs:212 +options.SendDefaultPii = true; +``` + +**影响**: +- `SendDefaultPii = true` 配置会收集和上报用户 IP 地址 +- 可能违反隐私法规(如 GDPR)要求 +- 在崩溃报告中可能暴露用户敏感信息 + +**修复建议**: +```csharp +options.SendDefaultPii = false; // 默认收集 PII +options.SendDefaultPii = TelemetryEnvironmentInfo.IsTelemetryPiiAllowed(); // 或根据用户同意状态动态设置 +``` + +--- + +## 未发现漏洞的区域 + +经过系统性审计,以下区域未发现中等及以上严重度的已确认漏洞: + +### 认证与访问控制 +- 单实例服务实现正确(使用互斥体) +- IPC 通信使用命名管道,无明显认证绕过风险 +- 插件隔离使用独立进程边界 + +### 注入向量 +- SQLite 使用参数化查询,无 SQL 注入风险 +- JSON 反序列化使用强类型上下文,无反序列化漏洞 +- 文件路径操作使用 `Path.Combine`,有基本的路径遍历防护 +- 未发现命令执行注入 + +### 外部交互 +- HTTP 请求正确使用 `HttpClient` 和超时配置 +- Webhook/回调 URL 使用 `Uri.EscapeDataString` 编码 +- 下载服务验证目标路径,无路径遍历风险 + +### 敏感数据处理 +- 数据库本地存储,使用 WAL 模式 +- 设置数据通过 JSON 序列化存储在用户目录 +- 日志文件路径正确隔离在应用数据目录 + +--- + +## 架构安全评估 + +| 组件 | 安全评级 | 说明 | +|------|----------|------| +| 插件系统 | 良好 | 使用独立进程隔离 | +| IPC 通信 | 良好 | 命名管道通信,进程边界隔离 | +| 更新系统 | 良好 | 支持签名验证 | +| 遥测系统 | **需改进** | 存在硬编码凭证和 PII 配置问题 | +| 数据存储 | 良好 | 使用标准加密实践 | + +--- + +## 修复优先级 + +| 优先级 | 漏洞 | 预计工作量 | +|--------|------|------------| +| P0 - 紧急 | #1 PostHog API Key | 低 | +| P0 - 紧急 | #2 Sentry DSN | 低 | +| P0 - 紧急 | #3 Xiaomi Weather Sign | 低 | +| P1 - 高 | #4 SendDefaultPii | 低 | + +--- + +## 建议的安全改进 + +1. **实施密钥管理**: 使用环境变量或密钥管理服务(如 Azure Key Vault、AWS Secrets Manager)存储所有 API 凭证 +2. **添加密钥扫描**: 在 CI/CD 流程中集成 secrets scanning(如 GitGuardian、trufflehog) +3. **隐私合规审查**: 确认遥测数据收集符合当地隐私法规要求 +4. **代码审计**: 建议进行定期安全审计 + +--- + +*报告生成工具: 自动安全审计系统* +*审计方法: 静态代码分析 + 架构审查* diff --git a/docs/auto_commit_md/20260511_SUMMARY.md b/docs/auto_commit_md/20260511_SUMMARY.md new file mode 100644 index 0000000..9e7c300 --- /dev/null +++ b/docs/auto_commit_md/20260511_SUMMARY.md @@ -0,0 +1,57 @@ +# Git 提交分析报告 - 2026-05-11 + +## 摘要 + +**日期**: 2026-05-11 +**新提交数量**: 0 +**状态**: ⚠️ 无新提交 + +--- + +## 详细说明 + +今天(2026-05-11)没有新的 Git 提交记录。 + +### 最近一次提交信息 + +- **提交哈希**: `d8f75e86be9054b29303dec01ec434ccb4db2b7f` +- **作者**: lincube +- **提交时间**: 2026-05-07 21:39:21 +0800 +- **提交信息**: Add IPC backoff/retries and safer disposal + +### 仓库状态 + +当前分支:`setting` +分支状态:与 `origin/setting` 保持同步 + +### 待提交更改 + +当前工作目录中存在以下未提交的更改: + +**已修改文件**: +- LanMountainDesktop/ViewModels/SettingsViewModels.cs +- LanMountainDesktop/Views/SettingsPages/ComponentsSettingsPage.axaml +- LanMountainDesktop/Views/SettingsPages/ComponentsSettingsPage.axaml.cs +- LanMountainDesktop/Views/SettingsPages/DataSettingsPage.axaml +- LanMountainDesktop/Views/SettingsPages/DataSettingsPage.axaml.cs +- LanMountainDesktop/Views/SettingsPages/LauncherSettingsPage.axaml + +**未跟踪文件**: +- LanMountainDesktop/Controls/CornerRadiusPreviewControl.cs +- LanMountainDesktop/Controls/GridPreviewControl.cs +- SECURITY_AUDIT_REPORT.md +- mockup-noise-level.html + +--- + +## 下一步建议 + +1. 如果有未提交的更改,请先提交 +2. 推送更改到远程仓库 +3. 明天再次运行此分析脚本 + +## 报告生成信息 + +- **生成时间**: 2026-05-11 +- **分析工具**: Git Commit Analyzer +- **输出目录**: docs/auto_commit_md/ diff --git a/mockup-noise-level.html b/mockup-noise-level.html new file mode 100644 index 0000000..1cb7f24 --- /dev/null +++ b/mockup-noise-level.html @@ -0,0 +1,898 @@ + + + + + +噪音等级组件改造 Mockup v2 + + + + + + +
+ 设计理念:不是整张图都铺满渐变色带,而是只在当前等级附近产生一个柔和的聚光灯光晕。 + 其余区域保持低调暗淡,让用户视线自然聚焦到当前所处等级。 + 当等级切换时,光晕平滑滑动到新位置,颜色同步过渡。 +

+ Quiet 安静 → + Normal 正常 → + Noisy 嘈杂 → + Extreme 极端 +  点击下方按钮切换等级,观察光晕移动效果 +
+ + +
+
🎛 模拟噪音等级切换
+
+
+
当前等级
+
+ + + + +
+
+
+
+ + +
+
当前实现 (Baseline)
+
四色硬切色带,各等级区域均匀着色,没有视觉焦点。用户需要主动寻找"我在哪个等级"。
+
+
+
+ 噪音等级分布 + Realtime +
+
+
+
+
+
+
+
+
+ Extreme + Noisy + Normal + Quiet +
+
+ + + + + + + + + + + + + + +
+
+ -12s + -6s + Now +
+
+
+
+
+ + +
+
聚光灯方案 — 局部渐变聚焦
+
只在当前等级附近产生柔和光晕,其余区域保持暗淡。等级切换时光晕平滑滑动,Y轴标签联动高亮,右侧指示条标注当前位置。下方条形图展示分布占比。
+
+ +
+
推荐
+
+ 噪音等级分布 + Realtime +
+
+ +
+
+
+ + +
+
+
+
+ + +
+ Extreme + Noisy + Normal + Quiet +
+ + +
+
+ + +
+ + + + + + + + + + + + + + +
+ +
+ -12s + -6s + Now +
+
+ + +
+
等级分布
+
+
+
+
+
+
+
+ 安静 35% + 正常 40% + 嘈杂 18% + 极端 7% +
+
+
+ + +
+
紧凑模式
+
+ 噪音等级分布 + Session +
+
+
+
+
+ +
+
+
+
+ +
+ Ext + Noisy + Norm + Quiet +
+ +
+
+ +
+ + + + +
+ +
+ -12s + -6s + Now +
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + +
+
四种等级状态一览
+
同时展示四个等级的聚光灯效果,可以直观对比光晕位置、颜色和强度的差异。
+
+ +
+
● Quiet 安静
+
+
+
+
+
+
+
+
+ Ext + Noisy + Norm + Quiet +
+
+
+
+ + + + + +
+
+
+ +
+
● Normal 正常
+
+
+
+
+
+
+
+
+ Ext + Noisy + Norm + Quiet +
+
+
+
+ + + + + +
+
+
+ +
+
● Noisy 嘈杂
+
+
+
+
+
+
+
+
+ Ext + Noisy + Norm + Quiet +
+
+
+
+ + + + + +
+
+
+ +
+
● Extreme 极端
+
+
+
+
+
+
+
+
+ Ext + Noisy + Norm + Quiet +
+
+
+
+ + + + + +
+
+
+
+
+ +@keyframes extremePulse { + 0%, 100% { opacity: 0.8; } + 50% { opacity: 1; } +} + + + + +