Skip to content

Fix AppLocker to WDAC conversion issues and improve macro handling#517

Open
D3vil0p3r wants to merge 2 commits into
MicrosoftDocs:mainfrom
D3vil0p3r:patch-1
Open

Fix AppLocker to WDAC conversion issues and improve macro handling#517
D3vil0p3r wants to merge 2 commits into
MicrosoftDocs:mainfrom
D3vil0p3r:patch-1

Conversation

@D3vil0p3r
Copy link
Copy Markdown

@D3vil0p3r D3vil0p3r commented May 2, 2026

Bug Fixes

  • When converting FileHashRule, all hashes in a rule were getting the same FriendlyName (the rule-level Name). Each hash now gets its own name from AppLocker XML SourceFileName field, making the output policy much easier to read and trace in event logs.
  • algo in ConvertFileHashRule was always read from index [0] of the condition array instead of the current hash in the loop. Harmless in practice since mixed hash types are rare, but wrong nonetheless.
  • FriendlyName was set from Description, which is optional and often empty in real-world AppLocker exports. Name is now always used as the primary label.
  • FilePathRule exceptions on Allow path rules are automatically converted to WDAC Deny path rules, with a warning that WDAC Deny overrides any Allow. Hash-based exceptions and exceptions on Deny path rules cannot be safely auto-converted. They are skipped and an actionable warning is emitted asking the user to handle them manually.
  • Parent path rules and their exceptions were sharing the same counter value, causing ID collisions like ID_ALLOW_C_7 and ID_DENY_C_7 coexisting in the same policy. I noted it didn't happen for other Rule Types.
  • DeserializeXMLtoPolicy and DeserializeXMLStringtoPolicy were swallowing exceptions and returning null silently, making it very hard to understand what went wrong during deserialization. They now throw with the original exception attached.
  • Deduplication of redundant path rules: when the same path appears as an exception across multiple AppLocker rule collections (Exe, Dll, Script etc.), the converter now automatically removes the duplicate Allow/Deny rules from the output WDAC policy instead of emitting them multiple times. A warning is emitted for each removed duplicate so the user is aware of what was consolidated.

Improvements

  • %PROGRAMFILES%, %PROGRAMFILES(X86)% and %PROGRAMDATA% are now automatically converted to their %OSDRIVE%-based WDAC equivalents instead of falling back to wildcard stripping. Per-user macros like %APPDATA% and %LOCALAPPDATA% correctly emit an actionable warning since they can't be safely mapped (in case someone is using them).
  • Updated target framework from net5.0 (EOL) to net10.0.

@D3vil0p3r D3vil0p3r marked this pull request as ready for review May 2, 2026 16:14
Copilot AI review requested due to automatic review settings May 2, 2026 16:14
@D3vil0p3r
Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

@D3vil0p3r
Copy link
Copy Markdown
Author

@jgeurten when you have some min free, can you please review this PR? It would close #516

Thanks

Copy link
Copy Markdown
Contributor

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

This PR improves the AppLocker → WDAC conversion tooling by fixing several rule-conversion correctness issues (friendly names, hash algorithm selection, path exceptions), enhancing path macro handling, and modernizing the project’s target framework.

Changes:

  • Improve rule labeling/correctness: use Name consistently for FriendlyName, fix per-hash algorithm selection, and add per-hash SourceFileName context.
  • Convert FilePathRule exceptions into WDAC-equivalent rules by inverting the parent action, and avoid ID collisions between parent/exception rules.
  • Improve path macro handling (convertible + user-scoped warnings) and update the project to net10.0.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 7 comments.

File Description
AppLocker-Policy-Converter/.../cipolicy.cs Removes unused generated fields from SiPolicy.
AppLocker-Policy-Converter/.../Helper.cs Fixes rule naming/hash conversion details, adds exception conversion for path rules, improves macro handling, and changes XML deserialization error behavior.
AppLocker-Policy-Converter/.../AppLocker-Policy-Converter.csproj Updates target framework to net10.0.

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

Copy link
Copy Markdown
Contributor

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

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

AppLocker-Policy-Converter/app/AppLocker-Policy-Converter/AppLocker-Policy-Converter/Helper.cs:756

  • In MakeValidPathRule, when a macro is found in convertibleMacros you set appLockerPathRule = wdacPathRule, but then execution continues into the "Unknown macro" fallback using the original macroParts, and the method returns the wildcard-stripped path. This means the deterministic conversion for %PROGRAMFILES% / %PROGRAMDATA% etc will never actually be returned. Fix by returning the converted wdacPathRule immediately (or recompute macroParts and re-run the supported-macro path).
                    if (convertibleMacros.TryGetValue(macroName, out string wdacMacroReplacement))
                    {
                        wdacPathRule = wdacMacroReplacement + macroParts[2];
                        WarningMessages.Add(String.Format(
                            "WARNING: AppLocker macro '%{0}%' is not supported in WDAC. " +
                            "Automatically converted \"{1}\" to \"{2}\".",
                            macroName, appLockerPathRule, wdacPathRule));

                        appLockerPathRule = wdacPathRule;
                    }

                    // Unknown macro - fall back to wildcard stripping as before
                    if (String.IsNullOrEmpty(macroParts[0]))
                    {
                        if(macroParts[2] == @"\*")
                        {
                            // E.g. %UNKNOWNMACRO%\* would result in Path=*\* or just Path="*" which we do not want to create
                            ErrorMessages.Add(String.Format("ERROR: AppLocker Path Rule \"{0}\" is not a valid WDAC Path Rule.", appLockerPathRule));
                            return null;
                        }

                        wdacPathRule = "*" + macroParts[2];
                    }
                    else
                    {
                        // Keep only the outside edges
                        wdacPathRule = macroParts[0] + macroParts[2];
                    }

                    WarningMessages.Add(String.Format("WARNING: AppLocker Path Rule \"{0}\" is not a valid WDAC Path Rule. Replacing with the " +
                        "following Path Rule: \"{1}\"", appLockerPathRule, wdacPathRule));

                    return wdacPathRule;

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

Copy link
Copy Markdown
Contributor

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

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.


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

Copy link
Copy Markdown
Contributor

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

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.


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

Copy link
Copy Markdown
Contributor

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

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


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

Copy link
Copy Markdown
Contributor

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

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


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

Copy link
Copy Markdown
Contributor

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

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.


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

Copy link
Copy Markdown
Contributor

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

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.


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

Copy link
Copy Markdown
Contributor

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

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


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

Copy link
Copy Markdown
Contributor

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

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.


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

Copy link
Copy Markdown
Contributor

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

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.


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

Copy link
Copy Markdown
Contributor

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

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


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

Copy link
Copy Markdown
Contributor

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

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.


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

Copy link
Copy Markdown
Contributor

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

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

Comments suppressed due to low confidence (1)

AppLocker-Policy-Converter/app/AppLocker-Policy-Converter/AppLocker-Policy-Converter/Helper.cs:735

  • SerializePolicytoXML still uses StreamWriter without a using/Dispose pattern. If serialization throws, the file handle may be left open and the output file can remain locked or partially written. Consider switching this to using var writer = new StreamWriter(...) (similar to the other deserialization helpers updated in this PR).
            // Serialize policy to XML file
            XmlSerializer serializer = new XmlSerializer(typeof(SiPolicy));
            StreamWriter writer = new StreamWriter(xmlPath);
            serializer.Serialize(writer, siPolicy);
            writer.Close();

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

Copy link
Copy Markdown
Contributor

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

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.


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

Copy link
Copy Markdown
Contributor

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

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.


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

Copy link
Copy Markdown
Contributor

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

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

Comments suppressed due to low confidence (1)

AppLocker-Policy-Converter/app/AppLocker-Policy-Converter/AppLocker-Policy-Converter/Helper.cs:739

  • SerializePolicytoXML manually closes the StreamWriter, but if serializer.Serialize(...) throws, the writer won't be disposed and the file handle can remain open. Use a using/using var for StreamWriter to ensure disposal even on exceptions.
            // Serialize policy to XML file
            XmlSerializer serializer = new XmlSerializer(typeof(SiPolicy));
            StreamWriter writer = new StreamWriter(xmlPath);
            serializer.Serialize(writer, siPolicy);
            writer.Close();

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

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

.NET 5.0 is EOL.

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.

2 participants