Skip to content

Add per-setting consequences page and surface impact/consequences in-app#10

Merged
Kvikku merged 4 commits intomainfrom
copilot/improve-docs-security-settings
Apr 18, 2026
Merged

Add per-setting consequences page and surface impact/consequences in-app#10
Kvikku merged 4 commits intomainfrom
copilot/improve-docs-security-settings

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 18, 2026

Users had no way to judge the compatibility risk of an individual setting or profile before applying it — neither in docs nor in the running tool.

Docs

  • New docs/security-setting-consequences.md: every one of the 98 built-in settings tabulated by category with a consistent 🟢 Low / 🟡 Medium / 🔴 High impact label and a short "Potential Consequences" note.
  • README.md, docs/getting-started.md, docs/security-profiles.md, and docs/backup-and-restore.md link the new page near their dry-run / backup guidance.
  • docs/security-profiles.md gains a "Likely side effects" line per profile; the Maximum Security entry enumerates the specific 🔴 High-impact settings it pulls in.
  • docs/extending-settings.md adds a maintenance rule: every new setting must ship with a catalog entry (or explicit Impact / Consequences) in the same PR, enforced by a unit test.

Runtime model

  • New ImpactLevel enum and Impact / Consequences properties on SecuritySetting (plain settable, default Unknown / "").
  • New SettingConsequencesCatalog keyed by setting ID, mirroring the docs page. SecuritySettingsManager enriches every setting from the catalog at construction; explicit values on a SecuritySetting are preserved.

CLI surfacing

  • list and detail show impact label and consequences per setting.
  • enable --dry-run shows per-change consequences plus a 🔴/🟡/🟢 summary and a review-before-applying warning.
  • profile --list shows a per-profile impact summary, e.g. Maximum Security … Impact: 🔴 5 High | 🟡 44 Medium | 🟢 49 Low.
  • profile --apply [--dry-run] shows per-change consequences and prints a rollback hint when Medium/High settings were enabled.

Interactive menu surfacing

  • List view: new Impact column.
  • Setting detail panel: Impact + Consequences rows.
  • Enable single-setting flow: ⚠ consequences printed before the confirmation prompt.
  • Profiles view: impact summary in the header, Impact column in the dry-run table, and an inline "Notable consequences" block listing every High/Medium-impact change; apply confirmation includes the impact breakdown.

Tests

  • SettingConsequencesCatalog_CoversEveryBuiltInSetting guards against missing entries for any built-in setting.
  • SettingConsequencesCatalog_DoesNotOverrideExplicitImpact guards the "explicit wins" rule.

Example CLI output:

[Maximum Security] (98 settings)
  Enables every available hardening setting...
  Impact: 🔴 5 High | 🟡 44 Medium | 🟢 49 Low

[DEF-007] Enable PUA Protection  (🟡 Medium)
  Enables Potentially Unwanted Application detection and blocking.
  ⚠ Consequences: Blocks 'potentially unwanted applications' — bundled
    installers, some adware-adjacent tools, certain freeware...

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a per-setting “impact/consequences” catalog (mirroring new documentation) and surfaces that metadata throughout the CLI and interactive TUI so users can assess compatibility risk before applying settings or profiles.

Changes:

  • Introduces ImpactLevel, plus Impact/Consequences on SecuritySetting, and enriches settings from a centralized SettingConsequencesCatalog.
  • Updates CLI commands (list, detail, enable --dry-run, profile) and the interactive menu to display impact labels, consequences, and profile-level summaries.
  • Adds comprehensive docs page + cross-links, plus unit tests enforcing catalog coverage and “explicit values win” behavior.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tests/WindowsSecurityManager.Tests/SettingDefinitionTests.cs Adds tests to enforce that all built-in settings have impact + consequences metadata and that explicit values aren’t overridden.
src/WindowsSecurityManager/UI/InteractiveMenu.cs Surfaces impact/consequences in list/detail/enable flows and adds profile impact summaries in the TUI.
src/WindowsSecurityManager/Commands/ProfileCommand.cs Adds per-profile impact summaries and prints per-setting impact/consequences during dry-run/apply flows.
src/WindowsSecurityManager/Commands/ListCommand.cs Shows impact and consequences in the CLI list output.
src/WindowsSecurityManager/Commands/ImpactFormatter.cs Adds a shared formatter for impact labels in plain-text CLI output.
src/WindowsSecurityManager/Commands/EnableCommand.cs Shows per-setting impact/consequences and prints an impact summary in dry-run output.
src/WindowsSecurityManager/Commands/DetailCommand.cs Displays impact and consequences in detail output.
src/WindowsSecurityManager.Core/Services/SecuritySettingsManager.cs Enriches settings with catalog-provided impact/consequences at manager construction.
src/WindowsSecurityManager.Core/Models/SecuritySetting.cs Adds Impact and Consequences properties to the runtime setting model.
src/WindowsSecurityManager.Core/Models/ImpactLevel.cs Introduces the ImpactLevel enum used across docs, CLI, and UI.
src/WindowsSecurityManager.Core/Definitions/SettingConsequencesCatalog.cs Adds the centralized ID→(impact, consequences) catalog and enrichment helpers.
docs/security-setting-consequences.md New per-setting consequences reference covering all built-in settings.
docs/security-profiles.md Links to consequences page and adds “Likely side effects” notes per profile.
docs/getting-started.md Adds guidance to review consequences before applying and links the new page.
docs/extending-settings.md Documents new model fields and adds a rule requiring consequences catalog/doc updates for new settings.
docs/backup-and-restore.md Links the new consequences page alongside backup/dry-run guidance.
README.md Adds link to the consequences page in the docs/usage sections.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +134 to +139
public static (ImpactLevel Impact, string Consequences) Get(string settingId)
{
if (settingId is not null && Entries.TryGetValue(settingId, out var entry))
return entry;
return (ImpactLevel.Unknown, string.Empty);
}
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With <Nullable>enable</Nullable>, Get(string settingId) declares settingId as non-nullable but then checks settingId is not null. Consider changing the parameter type to string? (or remove the null check) so the nullability contract matches the implementation and avoids redundant code / possible analyzer warnings.

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +12
/// Unknown / not classified. Treated like Low for display purposes but indicates
/// the setting has not been categorised yet.
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The XML doc for ImpactLevel.Unknown says it is "treated like Low for display purposes", but both CLI (ImpactFormatter) and TUI (FormatImpactMarkup) render it as an explicit "Unknown" label. Please align the documentation with the actual behavior, or update the formatters to match the documented behavior so users aren’t misled.

Suggested change
/// Unknown / not classified. Treated like Low for display purposes but indicates
/// the setting has not been categorised yet.
/// Unknown / not classified. Displayed as <c>Unknown</c> and indicates the
/// setting has not been categorised yet.

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +17
public static readonly IReadOnlyDictionary<string, (ImpactLevel Impact, string Consequences)> Entries =
new Dictionary<string, (ImpactLevel, string)>(StringComparer.OrdinalIgnoreCase)
{
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SettingConsequencesCatalog.Entries is exposed as an IReadOnlyDictionary, but it is backed by a mutable Dictionary instance. External callers can cast it back to Dictionary<,> and mutate the catalog at runtime. Consider returning a ReadOnlyDictionary/ImmutableDictionary (or keeping the backing dictionary private and exposing only lookup APIs) to prevent accidental modification.

Copilot uses AI. Check for mistakes.
Comment on lines +151 to +157
if (setting.Impact != ImpactLevel.Unknown && !string.IsNullOrEmpty(setting.Consequences))
continue;

var (impact, consequences) = Get(setting.Id);
if (setting.Impact == ImpactLevel.Unknown)
setting.Impact = impact;
if (string.IsNullOrEmpty(setting.Consequences))
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SettingConsequencesCatalog.Enrich treats only null/empty consequences as missing. If a provider accidentally sets Consequences to whitespace, enrichment won’t fill it from the catalog and the UI will show a blank note. Consider using string.IsNullOrWhiteSpace consistently for Consequences checks (and optionally skip null settings entries) to make enrichment more robust.

Suggested change
if (setting.Impact != ImpactLevel.Unknown && !string.IsNullOrEmpty(setting.Consequences))
continue;
var (impact, consequences) = Get(setting.Id);
if (setting.Impact == ImpactLevel.Unknown)
setting.Impact = impact;
if (string.IsNullOrEmpty(setting.Consequences))
if (setting is null)
continue;
if (setting.Impact != ImpactLevel.Unknown && !string.IsNullOrWhiteSpace(setting.Consequences))
continue;
var (impact, consequences) = Get(setting.Id);
if (setting.Impact == ImpactLevel.Unknown)
setting.Impact = impact;
if (string.IsNullOrWhiteSpace(setting.Consequences))

Copilot uses AI. Check for mistakes.
Comment on lines 53 to 65
foreach (var change in changes)
{
string current = change.IsCurrentlyConfigured ? change.CurrentValue?.ToString() ?? "N/A" : "NOT SET";
Console.WriteLine($" [{change.Setting.Id}] {change.Setting.Name}");
Console.WriteLine($" [{change.Setting.Id}] {change.Setting.Name} ({ImpactFormatter.Format(change.Setting.Impact)})");
Console.WriteLine($" {change.Setting.RegistryHive}\\{change.Setting.RegistryPath}\\{change.Setting.ValueName}");
Console.WriteLine($" Current: {current} → New: {change.NewValue}");
if (!string.IsNullOrWhiteSpace(change.Setting.Consequences))
Console.WriteLine($" ⚠ {change.Setting.Consequences}");
Console.WriteLine();
}
Console.WriteLine($"Total: {changes.Count} settings would be changed.");
PrintImpactSummary(changes.Select(c => c.Setting));
}
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new impact/consequences output in --dry-run isn’t covered by the existing command tests (they currently only assert IDs and totals). Consider adding/updating tests to assert that an impact label and (when present) a consequences line are printed, so regressions in this user-facing guidance are caught.

Copilot uses AI. Check for mistakes.
Comment on lines +102 to +104
if (high > 0 || medium > 0)
{
Console.WriteLine($"Impact: 🔴 {high} High | 🟡 {medium} Medium settings were enabled.");
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor formatting/wording issue: Impact: 🔴 {high} High | 🟡 {medium} Medium settings were enabled. is inconsistent with the other impact summaries (missing a | separator and the Low count, and the "Medium settings" phrasing reads oddly). Consider using the same High | Medium | Low summary format here for consistency.

Suggested change
if (high > 0 || medium > 0)
{
Console.WriteLine($"Impact: 🔴 {high} High | 🟡 {medium} Medium settings were enabled.");
int low = settings.Count(s => s.Impact == ImpactLevel.Low);
if (high > 0 || medium > 0)
{
Console.WriteLine($"Impact: 🔴 {high} High | 🟡 {medium} Medium | 🟢 {low} Low settings were enabled.");

Copilot uses AI. Check for mistakes.
@Kvikku
Copy link
Copy Markdown
Owner

Kvikku commented Apr 18, 2026

@copilot apply changes based on the comments in this thread

…hiteSpace, profile summary, doc fix, dry-run test

Agent-Logs-Url: https://github.com/Kvikku/Windows-Security-Manager/sessions/3d89f77d-f8f8-452d-85a7-277509e669cc

Co-authored-by: Kvikku <85877485+Kvikku@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 18, 2026

@copilot apply changes based on the comments in this thread

Applied all 6 review comments in 6442f75:

  • Get parameter is now string? to match the implementation
  • ImpactLevel.Unknown XML doc rewritten to match the actual Unknown rendering
  • Entries wrapped in ReadOnlyDictionary so the catalog can't be mutated by callers
  • Enrich uses IsNullOrWhiteSpace consistently and skips null entries
  • profile --apply summary uses the same 🔴 High | 🟡 Medium | 🟢 Low format as elsewhere
  • New Enable_DryRun_ShowsImpactLabelAndConsequences test asserts impact label, consequences line, and impact summary appear in dry-run output (125/125 tests pass)

Copilot AI requested a review from Kvikku April 18, 2026 17:43
@Kvikku
Copy link
Copy Markdown
Owner

Kvikku commented Apr 18, 2026

@copilot can you check the build fail and fix it?

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 18, 2026

@copilot can you check the build fail and fix it?

CI failure was dotnet format --verify-no-changes whitespace errors — wrapping the dictionary literal in new ReadOnlyDictionary<,>(new Dictionary<,>{...}) shifted the entries one indent level deeper than the formatter expected. Fixed by re-indenting all 98 catalog entries (dotnet format clean, 125/125 tests still pass) in 90e9ed3.

@Kvikku Kvikku marked this pull request as ready for review April 18, 2026 17:50
@Kvikku Kvikku merged commit 54c29e6 into main Apr 18, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants