diff --git a/WDAC-Policy-Wizard/app/MSIX/CreateScannedPolicy.ps1 b/WDAC-Policy-Wizard/app/MSIX/CreateScannedPolicy.ps1 index 467c71f2..a8eb792f 100644 --- a/WDAC-Policy-Wizard/app/MSIX/CreateScannedPolicy.ps1 +++ b/WDAC-Policy-Wizard/app/MSIX/CreateScannedPolicy.ps1 @@ -15,32 +15,38 @@ param ( [string]$UserPEs ) +# Convert comma-separated strings to arrays for PowerShell cmdlet parameters +$FallbackArray = $Fallback -split ',' +$OmitArray = if ($PathsToOmit -ne '') { $PathsToOmit -split ',' } else { @() } + # Run New-CIPolicy -Scan to generate a policy from a directory -# The command needs to be run twice to generate the full policy. Otherwise, the "An item with the same key has already been added." WARNING prevents the full policy from being generated. +# Use -WarningAction SilentlyContinue to suppress the "An item with the same key has already been added." warning +# which previously required running the command twice as a workaround. + +# Build optional splat for OmitPaths +$omitSplat = @{} +if ($OmitArray.Count -gt 0) { $omitSplat['OmitPaths'] = $OmitArray } + if($Deny -eq "False") { if($UserPEs -eq "True") { - New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $Fallback -OmitPaths $PathsToOmit -UserPEs - New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $Fallback -OmitPaths $PathsToOmit -UserPEs + New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $FallbackArray @omitSplat -UserPEs -WarningAction SilentlyContinue } else { - New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $Fallback -OmitPaths $PathsToOmit - New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $Fallback -OmitPaths $PathsToOmit + New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $FallbackArray @omitSplat -WarningAction SilentlyContinue } } else { if($UserPEs -eq "True") { - New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $Fallback -OmitPaths $PathsToOmit -UserPEs -Deny - New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $Fallback -OmitPaths $PathsToOmit -UserPEs -Deny + New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $FallbackArray @omitSplat -UserPEs -Deny -WarningAction SilentlyContinue } else { - New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $Fallback -OmitPaths $PathsToOmit -Deny - New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $Fallback -OmitPaths $PathsToOmit -Deny + New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $FallbackArray @omitSplat -Deny -WarningAction SilentlyContinue } } # SIG # Begin signature block diff --git a/WDAC-Policy-Wizard/app/Scripts/CreateScannedPolicy.ps1 b/WDAC-Policy-Wizard/app/Scripts/CreateScannedPolicy.ps1 index 467c71f2..f27b2a50 100644 --- a/WDAC-Policy-Wizard/app/Scripts/CreateScannedPolicy.ps1 +++ b/WDAC-Policy-Wizard/app/Scripts/CreateScannedPolicy.ps1 @@ -15,32 +15,37 @@ param ( [string]$UserPEs ) +# Convert comma-separated strings to arrays for PowerShell cmdlet parameters +$FallbackArray = $Fallback -split ',' +$OmitArray = if ($PathsToOmit -ne '') { $PathsToOmit -split ',' } else { @() } + # Run New-CIPolicy -Scan to generate a policy from a directory -# The command needs to be run twice to generate the full policy. Otherwise, the "An item with the same key has already been added." WARNING prevents the full policy from being generated. +# Use -WarningAction SilentlyContinue to suppress the "An item with the same key has already been added." warning +# which previously required running the command twice as a workaround. +# Build optional splat for OmitPaths +$omitSplat = @{} +if ($OmitArray.Count -gt 0) { $omitSplat['OmitPaths'] = $OmitArray } + if($Deny -eq "False") { if($UserPEs -eq "True") { - New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $Fallback -OmitPaths $PathsToOmit -UserPEs - New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $Fallback -OmitPaths $PathsToOmit -UserPEs + New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $FallbackArray @omitSplat -UserPEs -WarningAction SilentlyContinue } else { - New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $Fallback -OmitPaths $PathsToOmit - New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $Fallback -OmitPaths $PathsToOmit + New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $FallbackArray @omitSplat -WarningAction SilentlyContinue } } else { if($UserPEs -eq "True") { - New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $Fallback -OmitPaths $PathsToOmit -UserPEs -Deny - New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $Fallback -OmitPaths $PathsToOmit -UserPEs -Deny + New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $FallbackArray @omitSplat -UserPEs -Deny -WarningAction SilentlyContinue } else { - New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $Fallback -OmitPaths $PathsToOmit -Deny - New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $Fallback -OmitPaths $PathsToOmit -Deny + New-CIPolicy -ScanPath $ScanPath -Level $Level -FilePath $PolicyPath -Fallback $FallbackArray @omitSplat -Deny -WarningAction SilentlyContinue } } # SIG # Begin signature block diff --git a/WDAC-Policy-Wizard/app/src/CustomRuleConditionsPanel.Designer.cs b/WDAC-Policy-Wizard/app/src/CustomRuleConditionsPanel.Designer.cs index 9da2f1a8..9e3a9c24 100644 --- a/WDAC-Policy-Wizard/app/src/CustomRuleConditionsPanel.Designer.cs +++ b/WDAC-Policy-Wizard/app/src/CustomRuleConditionsPanel.Designer.cs @@ -154,6 +154,7 @@ private void InitializeComponent() panel_CustomRules.Margin = new System.Windows.Forms.Padding(2); panel_CustomRules.Name = "panel_CustomRules"; panel_CustomRules.Size = new System.Drawing.Size(615, 719); + panel_CustomRules.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; panel_CustomRules.TabIndex = 86; // // appIdPanel @@ -371,37 +372,43 @@ private void InitializeComponent() panelFolderScanConditions.Location = new System.Drawing.Point(552, 630); panelFolderScanConditions.Margin = new System.Windows.Forms.Padding(2); panelFolderScanConditions.Name = "panelFolderScanConditions"; - panelFolderScanConditions.Size = new System.Drawing.Size(537, 359); + panelFolderScanConditions.Size = new System.Drawing.Size(560, 340); + panelFolderScanConditions.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; panelFolderScanConditions.TabIndex = 126; panelFolderScanConditions.Visible = false; // // checkedListBoxOmitPaths // - checkedListBoxOmitPaths.Font = new System.Drawing.Font("Tahoma", 8F); + checkedListBoxOmitPaths.Font = new System.Drawing.Font("Tahoma", 8.5F); checkedListBoxOmitPaths.FormattingEnabled = true; - checkedListBoxOmitPaths.Location = new System.Drawing.Point(8, 260); + checkedListBoxOmitPaths.CheckOnClick = true; + checkedListBoxOmitPaths.HorizontalScrollbar = true; + checkedListBoxOmitPaths.IntegralHeight = false; + checkedListBoxOmitPaths.Location = new System.Drawing.Point(8, 250); checkedListBoxOmitPaths.Name = "checkedListBoxOmitPaths"; - checkedListBoxOmitPaths.Size = new System.Drawing.Size(341, 80); + checkedListBoxOmitPaths.Size = new System.Drawing.Size(500, 80); + checkedListBoxOmitPaths.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; checkedListBoxOmitPaths.TabIndex = 116; // // label12 // - label12.AutoSize = true; + label12.AutoSize = false; label12.Font = new System.Drawing.Font("Tahoma", 7F); label12.ForeColor = System.Drawing.Color.Black; - label12.Location = new System.Drawing.Point(6, 92); + label12.Location = new System.Drawing.Point(6, 88); label12.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); label12.Name = "label12"; - label12.Size = new System.Drawing.Size(469, 14); + label12.Size = new System.Drawing.Size(530, 32); + label12.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; label12.TabIndex = 115; - label12.Text = "Multiple levels can be selected. You can drag the levels to specify the fallback order."; + label12.Text = "First checked item = Level. Remaining checked items = Fallback.\r\nHash is always included as the final fallback for unsigned files."; // // label11 // label11.AutoSize = true; label11.Font = new System.Drawing.Font("Tahoma", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, 0); label11.ForeColor = System.Drawing.Color.Black; - label11.Location = new System.Drawing.Point(5, 232); + label11.Location = new System.Drawing.Point(5, 228); label11.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); label11.Name = "label11"; label11.Size = new System.Drawing.Size(231, 18); @@ -429,17 +436,21 @@ private void InitializeComponent() // checkedListBoxRuleLevels // checkedListBoxRuleLevels.AllowDrop = true; + checkedListBoxRuleLevels.CheckOnClick = true; checkedListBoxRuleLevels.Font = new System.Drawing.Font("Tahoma", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0); checkedListBoxRuleLevels.FormattingEnabled = true; checkedListBoxRuleLevels.Items.AddRange(new object[] { "PcaCertificate", "Publisher", "SignedVersion", "FilePublisher", "FileName", "FilePath", "Hash" }); - checkedListBoxRuleLevels.Location = new System.Drawing.Point(8, 121); + checkedListBoxRuleLevels.Location = new System.Drawing.Point(8, 125); checkedListBoxRuleLevels.MultiColumn = true; + checkedListBoxRuleLevels.ColumnWidth = 200; checkedListBoxRuleLevels.Name = "checkedListBoxRuleLevels"; - checkedListBoxRuleLevels.Size = new System.Drawing.Size(341, 88); + checkedListBoxRuleLevels.Size = new System.Drawing.Size(530, 100); + checkedListBoxRuleLevels.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; checkedListBoxRuleLevels.TabIndex = 111; checkedListBoxRuleLevels.DragDrop += RuleLevelsList_DragDropDone; checkedListBoxRuleLevels.DragOver += RuleLevelsList_DragInProgress; checkedListBoxRuleLevels.MouseDown += RuleLevelsList_MouseDown; + checkedListBoxRuleLevels.ItemCheck += RuleLevelsList_ItemCheck; // // label13 // @@ -1071,6 +1082,7 @@ private void InitializeComponent() button_CreateRule.Margin = new System.Windows.Forms.Padding(2); button_CreateRule.Name = "button_CreateRule"; button_CreateRule.Size = new System.Drawing.Size(110, 30); + button_CreateRule.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; button_CreateRule.TabIndex = 92; button_CreateRule.Text = "Create Rule"; button_CreateRule.UseVisualStyleBackColor = false; @@ -1083,6 +1095,7 @@ private void InitializeComponent() button_Next.Margin = new System.Windows.Forms.Padding(2); button_Next.Name = "button_Next"; button_Next.Size = new System.Drawing.Size(99, 30); + button_Next.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; button_Next.TabIndex = 107; button_Next.Text = "Next >"; button_Next.UseVisualStyleBackColor = false; @@ -1195,6 +1208,7 @@ private void InitializeComponent() button_AddException.Margin = new System.Windows.Forms.Padding(2); button_AddException.Name = "button_AddException"; button_AddException.Size = new System.Drawing.Size(110, 30); + button_AddException.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; button_AddException.TabIndex = 111; button_AddException.Text = "Add Exception"; button_AddException.UseVisualStyleBackColor = false; @@ -1209,6 +1223,7 @@ private void InitializeComponent() button_Back.Margin = new System.Windows.Forms.Padding(2); button_Back.Name = "button_Back"; button_Back.Size = new System.Drawing.Size(99, 30); + button_Back.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; button_Back.TabIndex = 110; button_Back.Text = "< Back"; button_Back.UseVisualStyleBackColor = false; @@ -1225,7 +1240,7 @@ private void InitializeComponent() AutoScaleDimensions = new System.Drawing.SizeF(120F, 120F); AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; BackColor = System.Drawing.Color.White; - ClientSize = new System.Drawing.Size(766, 828); + ClientSize = new System.Drawing.Size(900, 828); Controls.Add(controlHighlight_Panel); Controls.Add(button_AddException); Controls.Add(button_Back); diff --git a/WDAC-Policy-Wizard/app/src/CustomRuleConditionsPanel.cs b/WDAC-Policy-Wizard/app/src/CustomRuleConditionsPanel.cs index 579b9b3f..fc9d032a 100644 --- a/WDAC-Policy-Wizard/app/src/CustomRuleConditionsPanel.cs +++ b/WDAC-Policy-Wizard/app/src/CustomRuleConditionsPanel.cs @@ -60,6 +60,44 @@ public CustomRuleConditionsPanel(SigningRules_Control pControl) this.exceptionsControl = null; this.DefaultValues = new string[5]; this.FoundPackages = new List(); + CreateOmitPathsButtons(); + } + + /// + /// Creates Select All / Deselect All buttons above the omit paths list. + /// + private void CreateOmitPathsButtons() + { + var btnSelectAll = new Button + { + Text = "Select All", + Size = new System.Drawing.Size(80, 25), + Font = new System.Drawing.Font("Tahoma", 7.5F), + Location = new System.Drawing.Point(checkedListBoxOmitPaths.Right - 165, checkedListBoxOmitPaths.Top - 28), + Anchor = AnchorStyles.Top | AnchorStyles.Right + }; + btnSelectAll.Click += (s, ev) => + { + for (int i = 0; i < checkedListBoxOmitPaths.Items.Count; i++) + checkedListBoxOmitPaths.SetItemChecked(i, true); + }; + + var btnDeselectAll = new Button + { + Text = "Deselect All", + Size = new System.Drawing.Size(80, 25), + Font = new System.Drawing.Font("Tahoma", 7.5F), + Location = new System.Drawing.Point(checkedListBoxOmitPaths.Right - 80, checkedListBoxOmitPaths.Top - 28), + Anchor = AnchorStyles.Top | AnchorStyles.Right + }; + btnDeselectAll.Click += (s, ev) => + { + for (int i = 0; i < checkedListBoxOmitPaths.Items.Count; i++) + checkedListBoxOmitPaths.SetItemChecked(i, false); + }; + + panelFolderScanConditions.Controls.Add(btnSelectAll); + panelFolderScanConditions.Controls.Add(btnDeselectAll); } /// @@ -259,12 +297,13 @@ private void Button_CreateRule_Click(object sender, EventArgs e) { for (int i = 0; i < this.checkedListBoxRuleLevels.CheckedItems.Count; i++) { - this.PolicyCustomRule.Scan.Levels.Add(this.checkedListBoxRuleLevels.CheckedItems[i].ToString()); + string rawLevel = GetRawLevelName(this.checkedListBoxRuleLevels.CheckedItems[i].ToString()); + this.PolicyCustomRule.Scan.Levels.Add(rawLevel); } } // Check for Omit Scan Paths - if (this.checkedListBoxOmitPaths.CheckedItems.Count > 1) + if (this.checkedListBoxOmitPaths.CheckedItems.Count > 0) { // Using for loop to avoid System.InvalidOperationException despite list not changing for (int i = 0; i < this.checkedListBoxOmitPaths.CheckedItems.Count; i++) @@ -817,6 +856,7 @@ private void RuleType_ComboboxChanged(object sender, EventArgs e) this.panelFolderScanConditions.Location = this.checkBox_CustomPath.Location; this.panelFolderScanConditions.Visible = true; this.label_condition.Text = "Scan Path:"; + this.button_Next.Visible = false; break; case "Certificate File": @@ -2734,7 +2774,17 @@ private void LabelFolderScanLearnMore_Click(object sender, EventArgs e) /// private void RuleLevelsList_MouseDown(object sender, MouseEventArgs e) { - if (this.checkedListBoxRuleLevels.SelectedItem == null || e.X < 15 || (e.X > 150 && e.X < 165)) return; // e.X < 15 - left most column checkboxes. 150 < e.X < 165 - right most checkboxes + if (this.checkedListBoxRuleLevels.SelectedItem == null) return; + + // Determine checkbox width relative to each column + int columnWidth = this.checkedListBoxRuleLevels.ColumnWidth > 0 + ? this.checkedListBoxRuleLevels.ColumnWidth + : this.checkedListBoxRuleLevels.Width; + int xInColumn = e.X % columnWidth; + + // If click is in the checkbox area (first ~18px of each column), let CheckOnClick handle it + if (xInColumn < 18) return; + this.checkedListBoxRuleLevels.DoDragDrop(this.checkedListBoxRuleLevels.SelectedItem, DragDropEffects.Move); } @@ -2743,12 +2793,16 @@ private void RuleLevelsList_DragDropDone(object sender, DragEventArgs e) Point point = checkedListBoxRuleLevels.PointToClient(new Point(e.X, e.Y)); int index = this.checkedListBoxRuleLevels.IndexFromPoint(point); if (index < 0) index = this.checkedListBoxRuleLevels.Items.Count - 1; - bool isChecked = checkedListBoxRuleLevels.GetItemChecked(index); object data = checkedListBoxRuleLevels.SelectedItem; + int oldIndex = checkedListBoxRuleLevels.Items.IndexOf(data); + bool wasChecked = checkedListBoxRuleLevels.GetItemChecked(oldIndex); + this.checkedListBoxRuleLevels.Items.Remove(data); this.checkedListBoxRuleLevels.Items.Insert(index, data); - this.checkedListBoxRuleLevels.SetItemChecked(index, isChecked); + this.checkedListBoxRuleLevels.SetItemChecked(index, wasChecked); + + UpdateRuleLevelLabels(); } private void RuleLevelsList_DragInProgress(object sender, DragEventArgs e) @@ -2756,6 +2810,93 @@ private void RuleLevelsList_DragInProgress(object sender, DragEventArgs e) e.Effect = DragDropEffects.Move; } + /// + /// Updates the rule level items with (Level) and (Fallback) annotations, + /// ensures Hash is always checked as a fallback, and moves unchecked items to the back. + /// + private void UpdateRuleLevelLabels() + { + // Temporarily suppress events to avoid re-entrancy + this.checkedListBoxRuleLevels.ItemCheck -= RuleLevelsList_ItemCheck; + + // Collect items with their checked state + var checkedItems = new List(); + var uncheckedItems = new List(); + + for (int i = 0; i < checkedListBoxRuleLevels.Items.Count; i++) + { + string rawName = GetRawLevelName(checkedListBoxRuleLevels.Items[i].ToString()); + if (checkedListBoxRuleLevels.GetItemChecked(i)) + checkedItems.Add(rawName); + else + uncheckedItems.Add(rawName); + } + + // Ensure Hash is in checked list (unless it would be the only/first item making it Level) + if (!checkedItems.Contains("Hash")) + { + uncheckedItems.Remove("Hash"); + checkedItems.Add("Hash"); + } + + // Rebuild the list: checked items first, then unchecked + checkedListBoxRuleLevels.Items.Clear(); + + for (int i = 0; i < checkedItems.Count; i++) + { + string label = i == 0 + ? checkedItems[i] + " (Level)" + : checkedItems[i] + " (Fallback)"; + checkedListBoxRuleLevels.Items.Add(label); + checkedListBoxRuleLevels.SetItemChecked(checkedListBoxRuleLevels.Items.Count - 1, true); + } + + for (int i = 0; i < uncheckedItems.Count; i++) + { + checkedListBoxRuleLevels.Items.Add(uncheckedItems[i]); + } + + this.checkedListBoxRuleLevels.ItemCheck += RuleLevelsList_ItemCheck; + } + + /// + /// Strips the (Level) / (Fallback) annotation from item text to get the raw level name. + /// + private static string GetRawLevelName(string itemText) + { + return itemText.Replace(" (Level)", "").Replace(" (Fallback)", "").Trim(); + } + + /// + /// Handles item check state changes to update labels. + /// + private void RuleLevelsList_ItemCheck(object sender, ItemCheckEventArgs e) + { + // Prevent unchecking Hash when it's the forced fallback + string rawName = GetRawLevelName(checkedListBoxRuleLevels.Items[e.Index].ToString()); + if (rawName == "Hash" && e.NewValue == CheckState.Unchecked) + { + // Only allow unchecking if Hash is the Level (first checked item) + bool isLevel = true; + for (int i = 0; i < e.Index; i++) + { + if (checkedListBoxRuleLevels.GetItemChecked(i)) + { + isLevel = false; + break; + } + } + if (!isLevel) + { + e.NewValue = CheckState.Checked; + return; + } + } + + // Use BeginInvoke to update labels after the check state has actually changed + this.BeginInvoke(new Action(() => UpdateRuleLevelLabels())); + } + /// /// Opens the AppIdTagging Microsoft Learn doc diff --git a/WDAC-Policy-Wizard/app/src/MainForm.cs b/WDAC-Policy-Wizard/app/src/MainForm.cs index 1c81bcd1..3eb9600a 100644 --- a/WDAC-Policy-Wizard/app/src/MainForm.cs +++ b/WDAC-Policy-Wizard/app/src/MainForm.cs @@ -853,10 +853,10 @@ private void BackgroundWorker1_ProgressChanged(object sender, ProgressChangedEve int progressPercent = e.ProgressPercentage; if (progressPercent <= 10) process = "Building policy rules ..."; - else if (progressPercent <= 70) + else if (progressPercent <= 25) process = "Configuring policy signer and file rules ..."; else if (progressPercent <= 80) - process = "Building custom policy file rules ..."; + process = "Scanning and processing rules (this may take a few minutes) ..."; else if (progressPercent <= 85) process = "Merging custom rules policies ..."; else if (progressPercent <= 95) @@ -1252,9 +1252,6 @@ public SiPolicy ProcessSignerRules(BackgroundWorker worker, SiPolicy siPolicy) // Iterate through all of the custom rules and update the progress bar for (int i = 0; i < nCustomRules; i++) { - progressVal = 25 + i * 60 / nCustomRules; - worker.ReportProgress(progressVal); //Assumes the operations involved with this step take about 70% -- probably should be a little higher - var customRule = this.Policy.CustomRules[i]; // Skip; already handled ALL custom value rules @@ -1273,6 +1270,9 @@ public SiPolicy ProcessSignerRules(BackgroundWorker worker, SiPolicy siPolicy) continue; } + progressVal = 25 + i * 60 / nCustomRules; + worker.ReportProgress(progressVal); + string tmpPolicyPath = Helper.GetUniquePolicyPath(this.TempFolderPath); // Create a single policy per rule using the Powershell cmdlets with Level=PCACertificate or Publisher @@ -1288,7 +1288,7 @@ public SiPolicy ProcessSignerRules(BackgroundWorker worker, SiPolicy siPolicy) siPolicy = PolicyHelper.MergePolicies(signerSiPolicy, siPolicy); } } - + // Hash Rules -- Invoke Powershell cmd to generate if(customRule.Type == PolicyCustomRules.RuleType.Hash) { @@ -1303,6 +1303,9 @@ public SiPolicy ProcessSignerRules(BackgroundWorker worker, SiPolicy siPolicy) // Folder Scan -- Invoke the New-CiPolicy PS cmd to generate a CI policy if(customRule.Type == PolicyCustomRules.RuleType.FolderScan) { + // Report a mid-range progress so the UI shows scanning activity + worker.ReportProgress(Math.Min(progressVal + 30, 80)); + SiPolicy signerSiPolicy; if (this.Policy._PolicyType == WDAC_Policy.PolicyType.BasePolicy) { @@ -1312,7 +1315,7 @@ public SiPolicy ProcessSignerRules(BackgroundWorker worker, SiPolicy siPolicy) { signerSiPolicy = PSCmdlets.CreateScannedPolicyFromPS(customRule, tmpPolicyPath, this.Policy.BaseToSupplementPath); } - + // Successful Scan completed if (signerSiPolicy != null) { diff --git a/WDAC-Policy-Wizard/app/src/PSCmdlets.cs b/WDAC-Policy-Wizard/app/src/PSCmdlets.cs index 557e6c05..de2eb9ce 100644 --- a/WDAC-Policy-Wizard/app/src/PSCmdlets.cs +++ b/WDAC-Policy-Wizard/app/src/PSCmdlets.cs @@ -155,7 +155,12 @@ internal static SiPolicy CreateScannedPolicyFromPS(PolicyCustomRules customRule, // Add fallback levels, if applicable if (customRule.Scan.Levels.Count > 1) { - fallbacks = string.Join(",",customRule.Scan.Levels.Skip(1)); + fallbacks = string.Join(",", customRule.Scan.Levels.Skip(1)); + // Always ensure Hash is the final fallback for unsigned files + if (!fallbacks.Contains("Hash", StringComparison.OrdinalIgnoreCase)) + { + fallbacks += ",Hash"; + } } // Add paths to omit, if applicable @@ -176,7 +181,7 @@ internal static SiPolicy CreateScannedPolicyFromPS(PolicyCustomRules customRule, deny = "True"; } - string newPolicyScriptCmd = $"-NoProfile -ExecutionPolicy Bypass -File \"{ps1File}\" -ScanPath \"{scanPath}\" " + + string newPolicyScriptCmd = $"-NoProfile -NoLogo -NonInteractive -ExecutionPolicy Bypass -File \"{ps1File}\" -ScanPath \"{scanPath}\" " + $"-PolicyPath \"{policyPath}\" -Level {level} -Fallback {fallbacks} -PathsToOmit \"{pathsToOmit}\"" + $" -Deny {deny} -UserPEs {userPEs}"; @@ -201,10 +206,10 @@ internal static SiPolicy CreateScannedPolicyFromPS(PolicyCustomRules customRule, try { process.Start(); - process.WaitForExit(); - + // Read streams asynchronously to avoid deadlocks and allow PS to flush output string output = process.StandardOutput.ReadToEnd(); string error = process.StandardError.ReadToEnd(); + process.WaitForExit(); if (!string.IsNullOrEmpty(error)) {