From 1aa53091e9513d30d88629f2627b3aedf6b542e7 Mon Sep 17 00:00:00 2001 From: KBT096 <45223710+KBT096@users.noreply.github.com> Date: Sun, 30 Aug 2026 04:06:00 +0800 Subject: [PATCH] feat: expand read-only security audit for v1.4.0 --- CHANGELOG.md | 17 ++- PROJECT_SUMMARY.md | 6 +- README.en.md | 9 +- README.md | 9 +- docs/WINDOWS_VALIDATION.md | 6 +- scripts/Test-Repository.cmd | 22 ++++ src/WinSecure.cs | 227 +++++++++++++++++++++++++++++--- src/WindowsSecureToolkit.csproj | 4 + 8 files changed, 270 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58ed5a2..72725cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,20 @@ All notable changes are documented here. Versions follow Semantic Versioning. No unreleased changes. +## [1.4.0] - 2026-08-30 + +### Added + +- Added read-only system-volume BitLocker and UEFI Secure Boot status to the audit. +- Added `audit --json` for machine-readable audit output on standard output. +- Added smoke-test and self-test coverage for the new audit fields and status mappings. +- Aligned the EXE assembly and file metadata with toolkit version 1.4.0 and added a self-test guard. + +### Compatibility + +- Restore now accepts valid v1.3.2 and v1.3.3 backup manifests in addition to the existing compatible versions. +- An unavailable BitLocker provider or insufficient read permission is reported as `Unavailable`, not as an unencrypted volume. + ## [1.3.3] - 2026-08-28 ### Fixed @@ -89,7 +103,8 @@ Build, launch, version, self-test, audit, plan, report generation, listener list The first public Windows toolkit layout and safety documentation. -[Unreleased]: https://github.com/KBT096/windows-secure-toolkit/compare/v1.3.3...HEAD +[Unreleased]: https://github.com/KBT096/windows-secure-toolkit/compare/v1.4.0...HEAD +[1.4.0]: https://github.com/KBT096/windows-secure-toolkit/releases/tag/v1.4.0 [1.3.3]: https://github.com/KBT096/windows-secure-toolkit/releases/tag/v1.3.3 [1.3.2]: https://github.com/KBT096/windows-secure-toolkit/releases/tag/v1.3.2 [1.3.1]: https://github.com/KBT096/windows-secure-toolkit/releases/tag/v1.3.1 diff --git a/PROJECT_SUMMARY.md b/PROJECT_SUMMARY.md index 6cfe40c..953a8a0 100644 --- a/PROJECT_SUMMARY.md +++ b/PROJECT_SUMMARY.md @@ -18,11 +18,12 @@ `src/WinSecure.cs` 负责: -- 系统能力探测与只读审计; +- 系统能力探测与只读审计,包括 BitLocker 与 UEFI 安全启动状态; - 注册表、Guest、SMBv1、RDP/NLA 和防火墙策略的备份与恢复; - 保守基线的预览、确认、执行和逐项结果; - Defender 扫描、DISM/SFC 验证、监听端口与版本检查; - 只读 `doctor` 兼容性诊断及其 JSON 输出; +- 只读 `audit --json` 自动化输出; - 只读 `backups` 备份目录和清单状态查看; - 交互菜单、命令行参数和无修改自检。 @@ -51,6 +52,7 @@ - 真实 `cmd.exe` 入口; - 版本、帮助和计划模式; - 审计报告生成与 JSON 文件; +- `audit --json` 标准输出及 BitLocker、安全启动字段; - `doctor` 人类可读和 `--json` 两种输出; - `backups --json` 备份目录状态输出; - 缺少恢复路径时的错误码; @@ -70,7 +72,7 @@ - 扩展真实 Windows Server 版本测试矩阵; - 让 `doctor` 输出成为问题报告前的统一环境快照; -- 为审计 JSON 提供稳定 schema 与兼容性测试; +- 继续为审计 JSON 保持稳定 schema 与兼容性测试; - 增加由用户明确选择的单项配置,而不是扩大默认基线; - 根据真实 Issue、PR 和运行反馈调整检查项; - 为 Release 提供可追溯的编译附件,并保留源代码构建路径。 diff --git a/README.en.md b/README.en.md index 18582b7..ad872b6 100644 --- a/README.en.md +++ b/README.en.md @@ -7,14 +7,15 @@ Windows security settings are a bit like the fuse box: nobody wants to stare at it all day, but a small record is useful when something goes wrong. -Current version: `1.3.3`. +Current version: `1.4.0`. This is a small local toolkit with a CMD/BAT entry point and a C# engine. It checks, previews, backs up, and restores a conservative set of settings. It does not promise a magic “secure” button. Sadly, those are still out of stock. ## Core architecture and features -- read-only checks for firewall, Defender, UAC, SMBv1, Guest, RDP/NLA, AutoRun, updates, and pending reboot; +- read-only checks for firewall, Defender, UAC, SMBv1, Guest, RDP/NLA, AutoRun, BitLocker, Secure Boot, updates, and pending reboot; - Markdown and JSON audit reports; +- `audit --json` for machine-readable output on standard output; - a preview-first baseline with explicit elevation and confirmation; - a SHA-256 checked manifest and firewall backup before changes; - same-machine, allowlisted restore; @@ -44,6 +45,7 @@ No SDK? Open the [latest Release](https://github.com/KBT096/windows-secure-toolk build.cmd win_secure.cmd self-test win_secure.cmd audit +win_secure.cmd audit --json win_secure.cmd plan win_secure.cmd doctor win_secure.cmd apply @@ -51,6 +53,8 @@ win_secure.cmd apply Run `apply` from an elevated CMD only after reading the plan. Restore example: +BitLocker status can require an elevated CMD on some systems. `Unavailable` means that the state was not confirmed; it does not mean that the volume is unencrypted. + ```cmd win_secure.cmd restore "C:\ProgramData\WindowsSecureToolkit\Backups\20260818-120000" ``` @@ -61,6 +65,7 @@ win_secure.cmd restore "C:\ProgramData\WindowsSecureToolkit\Backups\20260818-120 | --- | --- | | `win_secure.cmd` | Open the menu | | `win_secure.cmd audit [path]` | Write Markdown + JSON reports | +| `win_secure.cmd audit --json` | Write the read-only audit JSON to standard output | | `win_secure.cmd plan` | Preview only | | `win_secure.cmd apply [--yes]` | Back up and apply the baseline | | `win_secure.cmd restore ` | Validate and restore a backup | diff --git a/README.md b/README.md index 20a9e5d..e310cdc 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,15 @@ Windows 的安全设置有点像家里的电箱:平时没人想看,真出问题又希望它有记录。 -当前版本:`1.3.3`。 +当前版本:`1.4.0`。 这个小工具从 CMD/BAT 进去,用 C# 做检查、预览、备份和恢复。它不负责把电脑变成“绝对安全”,只负责把常见的几件事做得清楚一点。 ## 核心架构与功能 -- 只读审计:防火墙、Defender、UAC、SMBv1、Guest、RDP/NLA、AutoRun、更新服务和待重启状态; +- 只读审计:防火墙、Defender、UAC、SMBv1、Guest、RDP/NLA、AutoRun、BitLocker、安全启动、更新服务和待重启状态; - 生成 Markdown 和 JSON 报告; +- 支持 `audit --json` 将机器可读结果直接输出到标准输出; - 预览一套保守基线,确认以后才应用; - 应用前保存清单、SHA-256 和防火墙策略; - 在同一台电脑上校验后恢复备份; @@ -58,10 +59,11 @@ win_secure.cmd self-test ```cmd win_secure.cmd doctor win_secure.cmd audit +win_secure.cmd audit --json win_secure.cmd plan ``` -`doctor` 检查系统兼容性,`audit` 生成 Markdown 和 JSON 报告,`plan` 显示将要处理的项目和可能的影响。 +`doctor` 检查系统兼容性,`audit` 生成 Markdown 和 JSON 报告,`audit --json` 为自动化输出纯 JSON,`plan` 显示将要处理的项目和可能的影响。BitLocker 状态在部分系统上需要从提升后的 CMD 读取;`Unavailable` 表示没有确认到状态,不等于磁盘未加密。 ### 第三步:确认后应用设置 @@ -93,6 +95,7 @@ win_secure.cmd restore "C:\ProgramData\WindowsSecureToolkit\Backups\20260818-120 | --- | --- | | `win_secure.cmd` | 打开菜单 | | `win_secure.cmd audit [路径]` | 生成 Markdown + JSON 审计报告 | +| `win_secure.cmd audit --json` | 将只读审计 JSON 输出到标准输出 | | `win_secure.cmd plan` | 预览,不修改系统 | | `win_secure.cmd apply [--yes]` | 备份并应用基线 | | `win_secure.cmd restore <路径>` | 校验并恢复备份 | diff --git a/docs/WINDOWS_VALIDATION.md b/docs/WINDOWS_VALIDATION.md index b47afb9..74721e8 100644 --- a/docs/WINDOWS_VALIDATION.md +++ b/docs/WINDOWS_VALIDATION.md @@ -21,9 +21,11 @@ The JSON form has `SchemaVersion`, `ToolkitVersion`, `GeneratedUtc`, `ComputerNa | CMD/BAT entry points | Not recorded in this matrix | Verified | Verified | The real Windows entry point forwards exit codes | | `self-test`, `version`, `help`, `plan` | Not recorded in this matrix | Verified | Verified | Core parsing and no-change checks run | | `doctor` and `doctor --json` | Not recorded in this matrix | Verified | Verified | Diagnostic output is available without system changes | -| Audit reports | Not recorded in this matrix | Verified | Verified | Markdown and JSON reports can be generated | +| Audit reports and `audit --json` | Not recorded in this matrix | Verified | Verified | Markdown, file JSON, and standard-output JSON can be generated | +| Secure Boot audit | Not recorded in this matrix | Verified | Smoke-tested | The registry-backed state is reported without changing firmware settings | +| BitLocker audit | Not recorded in this matrix | `Unavailable` without elevation | Smoke-tested | The command handles inaccessible or absent providers without treating them as an unencrypted result | | Apply / Restore | Verified by the maintainer | Verified by the maintainer | Not run | The real privileged paths were exercised locally on both versions; CI intentionally does not perform system-changing operations | -The maintainer has completed backed-up `Apply` and `Restore` validation on Windows 10 and Windows 11 through the normal elevated and confirmation flow. This is version-level evidence from the maintainer's test machines; it is not a claim that every Windows edition, policy combination, or managed environment behaves identically. A `Not recorded in this matrix` entry means that a separate result for that surface was not retained here; it does not mean the command is unsupported. +The maintainer has completed backed-up `Apply` and `Restore` validation on Windows 10 and Windows 11 through the normal elevated and confirmation flow. This is version-level evidence from the maintainer's test machines; it is not a claim that every Windows edition, policy combination, or managed environment behaves identically. A `Not recorded in this matrix` entry means that a separate result for that surface was not retained here; it does not mean the command is unsupported. For BitLocker, `Unavailable` means the current token or WMI provider did not expose the state; it is not a negative encryption finding. The CI job remains a smoke-test gate, not a privileged deployment test. If behavior differs on Windows 10 or Windows 11, open an [Issue](https://github.com/KBT096/windows-secure-toolkit/issues) with the toolkit version, Windows edition/build, elevation state, exact command, sanitized output, and whether the backup was retained. Do not attach secrets, raw audit reports, or private system identifiers. diff --git a/scripts/Test-Repository.cmd b/scripts/Test-Repository.cmd index 2132a76..0b9f751 100644 --- a/scripts/Test-Repository.cmd +++ b/scripts/Test-Repository.cmd @@ -59,6 +59,17 @@ call :run_ok_args backups --json if exist "%TEST_ROOT%" rmdir /s /q "%TEST_ROOT%" mkdir "%TEST_ROOT%" >nul 2>nul +call "%TOOL_ROOT%win_secure.cmd" audit --json > "%TEST_ROOT%\audit-stdout.json" +if errorlevel 1 ( + echo [失败] audit --json 命令失败。 + set /a FAILURES+=1 +) else ( + echo [完成] audit --json 命令通过。 + call :require_text "%TEST_ROOT%\audit-stdout.json" "SchemaVersion" + call :require_text "%TEST_ROOT%\audit-stdout.json" "bitlocker" + call :require_text "%TEST_ROOT%\audit-stdout.json" "secure-boot" +) + call "%TOOL_ROOT%win_secure.cmd" audit "%TEST_ROOT%" if errorlevel 1 ( echo [失败] 审计报告命令失败。 @@ -108,6 +119,17 @@ if errorlevel 1 ( ) exit /b 0 +:require_text +set /a CHECKS+=1 +findstr /i /c:"%~2" "%~1" >nul 2>nul +if errorlevel 1 ( + echo [失败] %~1 缺少文本:%~2 + set /a FAILURES+=1 +) else ( + echo [完成] %~1 包含文本:%~2 +) +exit /b 0 + :run_ok set /a CHECKS+=1 call "%TOOL_ROOT%win_secure.cmd" %~1 >nul diff --git a/src/WinSecure.cs b/src/WinSecure.cs index 3d490ec..fdc3ee1 100644 --- a/src/WinSecure.cs +++ b/src/WinSecure.cs @@ -21,12 +21,14 @@ namespace WindowsSecureToolkit { internal static class Program { - private const string Version = "1.3.3"; + private const string Version = "1.4.0"; private const string ToolkitName = "Windows Secure Toolkit"; private const string ReleaseApiUrl = "https://api.github.com/repos/KBT096/windows-secure-toolkit/releases/latest"; private static readonly HashSet SupportedBackupVersions = new HashSet(StringComparer.OrdinalIgnoreCase) { Version, + "1.3.3", + "1.3.2", "1.3.1", "1.3.0", "1.2.0", @@ -74,7 +76,7 @@ private static int Main(string[] args) switch (options.Action) { case "menu": return ShowMenu(); - case "audit": return RunAudit(options.ReportPath); + case "audit": return RunAudit(options.ReportPath, options.JsonOutput); case "apply": return RunApply(options.DryRun, options.Yes); case "restore": if (string.IsNullOrWhiteSpace(options.BackupPath)) @@ -135,6 +137,7 @@ private static void ShowHelp() Console.WriteLine(); Console.WriteLine(" menu 打开交互菜单"); Console.WriteLine(" audit [报告目录或 .md] 生成只读 Markdown + JSON 报告"); + Console.WriteLine(" audit --json 将只读审计 JSON 输出到标准输出"); Console.WriteLine(" plan 预览基线,不修改系统"); Console.WriteLine(" apply [--yes] 备份并交互式应用基线"); Console.WriteLine(" restore <备份目录或清单> 校验并恢复本工具管理的设置"); @@ -170,7 +173,7 @@ private static int ShowMenu() string choice = Console.ReadLine(); switch (choice) { - case "1": return RunAudit(null); + case "1": return RunAudit(null, false); case "2": return RunApply(true, false); case "3": return RunApply(false, false); case "4": @@ -237,6 +240,8 @@ public static CliOptions Parse(string[] args) else throw new ArgumentException("无法识别选项:" + arg); } if (result.Action == "plan") { result.Action = "apply"; result.DryRun = true; } + if (result.Action == "audit" && result.JsonOutput && !string.IsNullOrWhiteSpace(result.ReportPath)) + throw new ArgumentException("audit --json 不能同时指定报告路径。"); return result; } @@ -1062,11 +1067,142 @@ private sealed class AuditDocument public List Items; } + private sealed class BitLockerState + { + public string DriveLetter; + public int? ProtectionStatus; + public int? ConversionStatus; + public int? EncryptionMethod; + public string Error; + } + + private sealed class SecureBootState + { + public bool? Enabled; + public string Detail; + } + private static AuditItem Item(string status, string category, string id, string summary, string detail = "", string recommendation = "") { return new AuditItem { Status = status, Category = category, Id = id, Summary = summary, Detail = detail, Recommendation = recommendation }; } + private static BitLockerState ReadBitLockerState() + { + string systemDrive = Environment.GetEnvironmentVariable("SystemDrive"); + var state = new BitLockerState + { + DriveLetter = string.IsNullOrWhiteSpace(systemDrive) ? null : systemDrive.TrimEnd('\\') + }; + if (string.IsNullOrWhiteSpace(state.DriveLetter)) + { + state.Error = "无法确定 Windows 系统卷。"; + return state; + } + + try + { + using (var searcher = new ManagementObjectSearcher( + "root\\CIMV2\\Security\\MicrosoftVolumeEncryption", + "SELECT DriveLetter, ProtectionStatus, ConversionStatus, EncryptionMethod FROM Win32_EncryptableVolume")) + using (ManagementObjectCollection results = searcher.Get()) + { + ManagementObject volume = results.Cast().FirstOrDefault(item => + state.DriveLetter.Equals(Convert.ToString(item["DriveLetter"]), StringComparison.OrdinalIgnoreCase)); + if (volume == null) + { + state.Error = "BitLocker 提供程序没有返回系统卷。"; + return state; + } + + if (volume["ProtectionStatus"] != null) state.ProtectionStatus = Convert.ToInt32(volume["ProtectionStatus"]); + if (volume["ConversionStatus"] != null) state.ConversionStatus = Convert.ToInt32(volume["ConversionStatus"]); + if (volume["EncryptionMethod"] != null) state.EncryptionMethod = Convert.ToInt32(volume["EncryptionMethod"]); + } + } + catch (UnauthorizedAccessException) + { + state.Error = "当前权限无法读取 BitLocker 状态;可从提升后的 CMD 重新运行 audit。"; + } + catch (ManagementException ex) + { + state.Error = ex.ErrorCode == ManagementStatus.AccessDenied + ? "当前权限无法读取 BitLocker 状态;可从提升后的 CMD 重新运行 audit。" + : "BitLocker WMI 提供程序不可用:" + ex.Message; + } + catch (Exception ex) + { + state.Error = "BitLocker 状态读取失败:" + ex.Message; + } + return state; + } + + private static SecureBootState ReadSecureBootState() + { + try + { + RegistrySnapshot snapshot = ReadDword( + "HKLM\\SYSTEM\\CurrentControlSet\\Control\\SecureBoot\\State", + "UEFISecureBootEnabled"); + if (!snapshot.Exists) + { + return new SecureBootState + { + Detail = "未找到 UEFISecureBootEnabled;设备可能使用传统 BIOS,或当前 Windows 环境不公开该状态。" + }; + } + if (snapshot.Kind != "DWord" || (snapshot.Value != 0 && snapshot.Value != 1)) + { + return new SecureBootState + { + Detail = "UEFISecureBootEnabled 的类型或值不在预期范围内。" + }; + } + return new SecureBootState + { + Enabled = snapshot.Value == 1, + Detail = "UEFISecureBootEnabled=" + snapshot.Value + }; + } + catch (Exception ex) + { + return new SecureBootState { Detail = "安全启动状态读取失败:" + ex.Message }; + } + } + + private static AuditItem CreateBitLockerAuditItem(BitLockerState state) + { + if (state == null || !state.ProtectionStatus.HasValue || !state.ConversionStatus.HasValue) + { + return Item( + "Unavailable", + "磁盘", + "bitlocker", + "无法确认系统卷 BitLocker 状态", + state == null ? "BitLocker 状态不可用。" : state.Error, + "Unavailable 不表示磁盘未加密;必要时从提升后的 CMD 重新审计。"); + } + + string detail = "Drive=" + state.DriveLetter + + "; ProtectionStatus=" + state.ProtectionStatus + + "; ConversionStatus=" + state.ConversionStatus + + "; EncryptionMethod=" + (state.EncryptionMethod.HasValue ? state.EncryptionMethod.Value.ToString() : "未知"); + if (state.ProtectionStatus == 1 && state.ConversionStatus == 1) + return Item("Pass", "磁盘", "bitlocker", "系统卷 BitLocker 保护已开启且已完成加密", detail); + if (state.ProtectionStatus == 0 && state.ConversionStatus == 0) + return Item("Review", "磁盘", "bitlocker", "系统卷 BitLocker 保护未开启", detail, "确认恢复密钥保存位置后,再按设备与组织策略评估是否启用。"); + return Item("Review", "磁盘", "bitlocker", "系统卷 BitLocker 状态需要复核", detail, "检查加密进度、保护器和恢复密钥保管状态。"); + } + + private static AuditItem CreateSecureBootAuditItem(SecureBootState state) + { + if (state == null || !state.Enabled.HasValue) + return Item("Unavailable", "启动", "secure-boot", "无法确认安全启动状态", state == null ? "安全启动状态不可用。" : state.Detail); + return state.Enabled.Value + ? Item("Pass", "启动", "secure-boot", "UEFI 安全启动已开启", state.Detail) + : Item("Review", "启动", "secure-boot", "UEFI 安全启动未开启", state.Detail, "确认固件模式、磁盘布局和兼容性后,再在固件设置中评估是否启用。"); + } + private static List CollectAudit() { var items = new List(); @@ -1117,8 +1253,8 @@ private static List CollectAudit() bool pending = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootPending") != null || Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update\\RebootRequired") != null; items.Add(Item(pending ? "Info" : "Pass", "维护", "pending-reboot", pending ? "检测到待重启状态" : "未检测到待重启状态")); - items.Add(Item("Unavailable", "磁盘", "bitlocker", "当前版本不读取 BitLocker 状态")); - items.Add(Item("Unavailable", "启动", "secure-boot", "当前版本不读取安全启动状态")); + items.Add(CreateBitLockerAuditItem(ReadBitLockerState())); + items.Add(CreateSecureBootAuditItem(ReadSecureBootState())); items.Add(Item("Info", "网络", "listening-ports", "监听端口请使用 ports 命令查看;监听不等于公网开放")); return items; } @@ -1367,23 +1503,43 @@ private static string FindExecutable(string fileName) catch { return null; } } - private static int RunAudit(string requestedPath) + private static AuditDocument CreateAuditDocument(List items) { - Section("只读安全审计"); - Message("信息", "正在读取本机安全配置,不修改系统设置。"); + return new AuditDocument + { + GeneratedUtc = DateTime.UtcNow.ToString("o"), + ComputerName = Environment.MachineName, + Items = items + }; + } + + private static int RunAudit(string requestedPath, bool jsonOutput) + { + if (!jsonOutput) + { + Section("只读安全审计"); + Message("信息", "正在读取本机安全配置,不修改系统设置。"); + } List items = CollectAudit(); + AuditDocument document = CreateAuditDocument(items); + if (jsonOutput) + { + Console.WriteLine(Json.Serialize(document)); + return 0; + } + foreach (AuditItem item in items) { Console.WriteLine("{0,-12} {1,-8} {2,-18} {3}", item.Status, item.Category, item.Id, item.Summary); } - string[] paths = WriteAuditReports(items, requestedPath); + string[] paths = WriteAuditReports(document, requestedPath); Message("完成", "Markdown 报告:" + paths[0]); Message("完成", "JSON 报告:" + paths[1]); if (items.Any(item => item.Status == "Review")) Message("警告", "有项目需要人工复核;这不等同于确认存在漏洞。"); return 0; } - private static string[] WriteAuditReports(List items, string requestedPath) + private static string[] WriteAuditReports(AuditDocument document, string requestedPath) { string directory; string markdownPath; @@ -1403,14 +1559,6 @@ private static string[] WriteAuditReports(List items, string requeste jsonPath = Path.Combine(directory, stem + ".json"); } Directory.CreateDirectory(directory); - var document = new AuditDocument - { - SchemaVersion = 1, - ToolkitVersion = Version, - GeneratedUtc = DateTime.UtcNow.ToString("o"), - ComputerName = Environment.MachineName, - Items = items - }; File.WriteAllText(jsonPath, Json.Serialize(document), new UTF8Encoding(false)); var markdown = new StringBuilder(); markdown.AppendLine("# Windows Secure Toolkit audit"); @@ -1421,7 +1569,7 @@ private static string[] WriteAuditReports(List items, string requeste markdown.AppendLine(); markdown.AppendLine("| Status | Category | Id | Summary | Detail |"); markdown.AppendLine("| --- | --- | --- | --- | --- |"); - foreach (AuditItem item in items) + foreach (AuditItem item in document.Items) { markdown.AppendLine("| " + SafeMarkdown(item.Status) + " | " + SafeMarkdown(item.Category) + " | " + SafeMarkdown(item.Id) + " | " + SafeMarkdown(item.Summary) + " | " + SafeMarkdown(item.Detail) + " |"); } @@ -1539,6 +1687,14 @@ private static int RunSelfTest() var failures = new List(); Version parsed; if (!System.Version.TryParse(Version, out parsed)) failures.Add("版本号不是有效的语义版本。"); + System.Version assemblyVersion = Assembly.GetExecutingAssembly().GetName().Version; + if (assemblyVersion == null + || assemblyVersion.Major != parsed.Major + || assemblyVersion.Minor != parsed.Minor + || assemblyVersion.Build != parsed.Build) + { + failures.Add("程序集版本与工具版本不一致。"); + } if (!ReleaseApiUrl.StartsWith("https://api.github.com/", StringComparison.OrdinalIgnoreCase)) failures.Add("Release API 地址不是受限的 HTTPS 地址。"); if (Quote("C:\\a b\\file.txt") != "\"C:\\a b\\file.txt\"") failures.Add("命令行路径引用测试失败。"); var expected = new RegistrySnapshot @@ -1598,11 +1754,42 @@ private static int RunSelfTest() } catch (Exception ex) { failures.Add("doctor 诊断测试失败:" + ex.Message); } - if (!IsSupportedBackupVersion("1.3.1") || !IsSupportedBackupVersion("1.3.0") || !IsSupportedBackupVersion("1.2.0") || !IsSupportedBackupVersion("1.2.1") || IsSupportedBackupVersion("0.1.0")) + string[] compatibleBackups = { "1.3.3", "1.3.2", "1.3.1", "1.3.0", "1.2.1", "1.2.0" }; + if (!compatibleBackups.All(IsSupportedBackupVersion) || !IsSupportedBackupVersion(Version) || IsSupportedBackupVersion("0.1.0")) { failures.Add("旧版本备份兼容性测试失败。"); } + try + { + AuditItem protectedVolume = CreateBitLockerAuditItem(new BitLockerState + { + DriveLetter = "C:", + ProtectionStatus = 1, + ConversionStatus = 1, + EncryptionMethod = 6 + }); + AuditItem unprotectedVolume = CreateBitLockerAuditItem(new BitLockerState + { + DriveLetter = "C:", + ProtectionStatus = 0, + ConversionStatus = 0, + EncryptionMethod = 0 + }); + AuditItem secureBootOn = CreateSecureBootAuditItem(new SecureBootState { Enabled = true, Detail = "self-test" }); + AuditItem secureBootOff = CreateSecureBootAuditItem(new SecureBootState { Enabled = false, Detail = "self-test" }); + AuditItem secureBootUnknown = CreateSecureBootAuditItem(new SecureBootState { Detail = "self-test" }); + if (protectedVolume.Status != "Pass" + || unprotectedVolume.Status != "Review" + || secureBootOn.Status != "Pass" + || secureBootOff.Status != "Review" + || secureBootUnknown.Status != "Unavailable") + { + failures.Add("BitLocker 或安全启动状态映射测试失败。"); + } + } + catch (Exception ex) { failures.Add("磁盘与启动审计测试失败:" + ex.Message); } + string temp = Path.Combine(Path.GetTempPath(), "windows-secure-toolkit-selftest-" + Guid.NewGuid().ToString("N")); try { diff --git a/src/WindowsSecureToolkit.csproj b/src/WindowsSecureToolkit.csproj index 6d774af..5b491b4 100644 --- a/src/WindowsSecureToolkit.csproj +++ b/src/WindowsSecureToolkit.csproj @@ -4,6 +4,10 @@ net48 WinSecure WindowsSecureToolkit + 1.4.0 + 1.4.0.0 + 1.4.0.0 + 1.4.0 latest disable disable