Skip to content

Add support for inline XML format data in Update-FormatData#27713

Open
eevaal wants to merge 8 commits into
PowerShell:masterfrom
eevaal:feat/update-formatdata-xml
Open

Add support for inline XML format data in Update-FormatData#27713
eevaal wants to merge 8 commits into
PowerShell:masterfrom
eevaal:feat/update-formatdata-xml

Conversation

@eevaal

@eevaal eevaal commented Jul 22, 2026

Copy link
Copy Markdown

PR Summary

This PR allows the Update-FormatData cmdlet to accept formatting configuration (PS1XML) directly from an in-memory string. It introduces two new string array parameters: -PrependFormatData and -AppendFormatData.

Under the hood:

  • Adds a new FormatDataXml property and an XmlDocument constructor to SessionStateFormatEntry for secure encapsulation of the raw string.
  • Updates TypeInfoDataBaseLoader to parse inline XML strings without requiring physical disk I/O.
  • Plumbs the inline XML capabilities into UpdateFormatDataCommand via the TypeInfoDataBaseManager.
  • Adds Pester unit tests to ensure that -PrependFormatData and -AppendFormatData correctly load views.

PR Context

Resolves #7845

Currently, module authors who use tools like ModuleBuilder to pack their modules into a single .psm1 file for better performance cannot easily embed their format data (.ps1xml file contents). They are forced to ship external .ps1xml files alongside the module, which also complicates code signing (since the XML file must be signed separately).

By allowing Update-FormatData to accept raw XML strings, module authors can now natively store and apply formatting rules entirely in memory.

PR Checklist

Addresses PowerShell#7845 by allowing Update-FormatData to accept format data directly as XML strings using the -PrependFormatData and -AppendFormatData parameters.

- Adds FormatDataXml property and constructor to SessionStateFormatEntry.

- Updates TypeInfoDataBaseLoader to parse format data XML from strings without disk I/O.

- Plumbs the inline XML capabilities into UpdateFormatDataCommand through TypeInfoDataBaseManager.

- Adds unit tests covering new parameters.
Copilot AI review requested due to automatic review settings July 22, 2026 16:19
@eevaal
eevaal requested a review from a team as a code owner July 22, 2026 16:19
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

- Add blank lines before doc comments

- Group fields before properties

- Add Justification to SuppressMessage attributes

- Use 'Gets or sets' in property summaries

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 extends Update-FormatData to accept PS1XML formatting configuration as in-memory XML strings, enabling module authors to embed format data without shipping external .ps1xml files (addressing #7845).

Changes:

  • Adds new Update-FormatData parameters -PrependFormatData / -AppendFormatData for inline format XML input.
  • Introduces SessionStateFormatEntry.FormatDataXml and plumbing through TypeInfoDataBaseManager/loader to load format data from XML strings.
  • Adds Pester coverage intended to validate loading views from inline XML.

Reviewed changes

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

Show a summary per file
File Description
test/powershell/Modules/Microsoft.PowerShell.Utility/Update-FormatData.Tests.ps1 Adds tests for loading format views from inline XML strings.
src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-TypeData.cs Adds cmdlet parameters and populates InitialSessionState.Formats with inline XML entries.
src/System.Management.Automation/engine/InitialSessionState.cs Adds FormatDataXml storage on SessionStateFormatEntry and cloning support.
src/System.Management.Automation/utils/FormatAndTypeDataHelper.cs Adds support for carrying inline format XML through PSSnapInTypeAndFormatErrors.
src/System.Management.Automation/FormatAndOutput/common/DisplayDatabase/typeDataXmlLoader.cs Adds LoadXmlString(...) to parse/load formatting data from an XML string.
src/System.Management.Automation/FormatAndOutput/common/DisplayDatabase/typeDataManager.cs Hooks inline XML loading into the format database load path.

Comment on lines +978 to +980
var doc = new System.Xml.XmlDocument();
doc.LoadXml(PrependFormatData[i]);
newFormats.Add(new SessionStateFormatEntry(doc));
Comment on lines +1018 to +1020
var doc = new System.Xml.XmlDocument();
doc.LoadXml(appendFormatDataItem);
newFormats.Add(new SessionStateFormatEntry(doc));
Comment on lines +369 to +373
else if (FormatDataXml != null)
{
var doc = new XmlDocument();
doc.LoadXml(FormatDataXml);
entry = new SessionStateFormatEntry(doc);
Comment on lines +48 to +53
internal PSSnapInTypeAndFormatErrors(string psSnapinName, string formatDataXml, bool isXml)
{
this.psSnapinName = psSnapinName;
FormatDataXml = formatDataXml;
Errors = new ConcurrentBag<string>();
}
Security (High): Replace unsafe XmlDocument.LoadXml() with XmlReader using DtdProcessing.Prohibit and XmlResolver=null to prevent XXE and DTD entity expansion attacks. Fixed in Update-TypeData.cs (Prepend + Append) and InitialSessionState.Clone().

Tests (Medium): Fix variable scoping in Pester tests by passing xmlContent via param()/AddArgument() instead of string interpolation.
@eevaal
eevaal force-pushed the feat/update-formatdata-xml branch from ad795c7 to 9b555f4 Compare July 22, 2026 19:40
@eevaal
eevaal requested a review from Copilot July 22, 2026 21:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 6 out of 6 changed files in this pull request and generated 5 comments.

Comments suppressed due to low confidence (1)

src/System.Management.Automation/utils/FormatAndTypeDataHelper.cs:52

  • The new PSSnapInTypeAndFormatErrors constructor takes a boolean parameter (isXml) that is not used. Consider either removing the parameter (and updating call sites) or renaming it to '_' to make the intentional discard explicit.
        internal PSSnapInTypeAndFormatErrors(string psSnapinName, string formatDataXml, bool isXml)
        {
            this.psSnapinName = psSnapinName;
            FormatDataXml = formatDataXml;
            Errors = new ConcurrentBag<string>();

Comment on lines +327 to +331
public SessionStateFormatEntry(XmlDocument xmlDocument)
: base("*")
{
if (xmlDocument == null)
{
Comment on lines +98 to 110
It "Should load inline XML using AppendFormatData" {
$null = $ps.AddScript('param($xml) Update-FormatData -AppendFormatData $xml').AddArgument($xmlContent)
$ps.Invoke()
$ps.HadErrors | Should -BeFalse
$ps.Commands.Clear()
$null = $ps.AddScript("Get-FormatData InlineTestType")
$formatData = $ps.Invoke()
$formatData | Should -HaveCount 1
$formatData.TypeNames | Should -BeExactly "InlineTestType"
$formatData.FormatViewDefinition.Name | Should -BeExactly "InlineTest"
}
}
}
Comment on lines +979 to +989
if (ShouldProcess("Inline XML", action))
{
var doc = new System.Xml.XmlDocument() { XmlResolver = null };
var settings = new System.Xml.XmlReaderSettings { DtdProcessing = System.Xml.DtdProcessing.Prohibit, XmlResolver = null };
using (var reader = System.Xml.XmlReader.Create(new System.IO.StringReader(PrependFormatData[i]), settings))
{
doc.Load(reader);
}

newFormats.Add(new SessionStateFormatEntry(doc));
}
Comment on lines +1024 to +1034
if (ShouldProcess("Inline XML", action))
{
var doc = new System.Xml.XmlDocument() { XmlResolver = null };
var settings = new System.Xml.XmlReaderSettings { DtdProcessing = System.Xml.DtdProcessing.Prohibit, XmlResolver = null };
using (var reader = System.Xml.XmlReader.Create(new System.IO.StringReader(appendFormatDataItem), settings))
{
doc.Load(reader);
}

newFormats.Add(new SessionStateFormatEntry(doc));
}
eevaal and others added 2 commits July 23, 2026 00:41
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 6 out of 6 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (4)

src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-TypeData.cs:989

  • Update-FormatData -PrependFormatData parses the XML via XmlDocument.Load(...) without handling XmlException. A malformed XML string will throw a terminating .NET exception before the cmdlet reaches its normal FormatXmlUpdateException handling, producing inconsistent errors (and bypassing -ErrorAction semantics). Catch parse failures and emit a cmdlet error record (and either continue or return).
                    if (ShouldProcess("Inline XML", action))
                    {
                        var doc = new System.Xml.XmlDocument() { XmlResolver = null };
                        var settings = new System.Xml.XmlReaderSettings { DtdProcessing = System.Xml.DtdProcessing.Prohibit, XmlResolver = null };
                        using (var reader = System.Xml.XmlReader.Create(new System.IO.StringReader(PrependFormatData[i]), settings))
                        {
                            doc.Load(reader);
                        }

                        newFormats.Add(new SessionStateFormatEntry(doc));
                    }

src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-TypeData.cs:1034

  • Update-FormatData -AppendFormatData has the same unhandled XML parse failure path as prepend: malformed XML throws out of ProcessRecord() as a raw XmlException instead of producing a FormatXmlUpdateException error record. Handle XmlException during parsing.
                    if (ShouldProcess("Inline XML", action))
                    {
                        var doc = new System.Xml.XmlDocument() { XmlResolver = null };
                        var settings = new System.Xml.XmlReaderSettings { DtdProcessing = System.Xml.DtdProcessing.Prohibit, XmlResolver = null };
                        using (var reader = System.Xml.XmlReader.Create(new System.IO.StringReader(appendFormatDataItem), settings))
                        {
                            doc.Load(reader);
                        }

                        newFormats.Add(new SessionStateFormatEntry(doc));
                    }

src/System.Management.Automation/utils/FormatAndTypeDataHelper.cs:54

  • The new PSSnapInTypeAndFormatErrors(string psSnapinName, string formatDataXml, bool isXml) overload has an unused parameter (_ = isXml;). This makes the API misleading and suggests incomplete implementation. Either remove the parameter and update call sites, or store/consume it meaningfully.
        internal PSSnapInTypeAndFormatErrors(string psSnapinName, string formatDataXml, bool isXml)
        {
            this.psSnapinName = psSnapinName;
            FormatDataXml = formatDataXml;
            _ = isXml;
            Errors = new ConcurrentBag<string>();
        }

test/powershell/Modules/Microsoft.PowerShell.Utility/Update-FormatData.Tests.ps1:108

  • The new inline XML feature is only tested for the happy path. Add a negative test that verifies malformed XML passed to -PrependFormatData/-AppendFormatData produces a FormatXmlUpdateException error record (consistent with the existing invalid .ps1xml file test).
        It "Should load inline XML using AppendFormatData" {
            $null = $ps.AddScript('param($xml) Update-FormatData -AppendFormatData $xml').AddArgument($xmlContent)
            $ps.Invoke()
            $ps.HadErrors | Should -BeFalse
            $ps.Commands.Clear()
            $null = $ps.AddScript("Get-FormatData InlineTestType")
            $formatData = $ps.Invoke()
            $formatData | Should -HaveCount 1
            $formatData.TypeNames | Should -BeExactly "InlineTestType"
            $formatData.FormatViewDefinition.Name | Should -BeExactly "InlineTest"
        }

Comment on lines +412 to +420
if (file.FormatDataXml != null)
{
XmlFileLoadInfo info =
new XmlFileLoadInfo("Inline", "Inline XML", file.Errors, file.PSSnapinName);
using (TypeInfoDataBaseLoader loader = new TypeInfoDataBaseLoader())
{
if (!loader.LoadXmlString(file.FormatDataXml, info, db, expressionFactory, preValidated))
success = false;

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 6 out of 6 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (4)

src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-TypeData.cs:985

  • Inline XML is parsed with XmlDocument.Load() without handling XmlException. If the XML is invalid, the XmlException will escape ProcessRecord() (it is not a RuntimeException), so the cmdlet won't revert InitialSessionState.Formats and won't emit the standard FormatXmlUpdateException error record (unlike the file-based path). Wrap the parse/load in a try/catch and rethrow as a PowerShell RuntimeException-derived type so existing error handling runs consistently.
                        var doc = new System.Xml.XmlDocument() { XmlResolver = null };
                        var settings = new System.Xml.XmlReaderSettings { DtdProcessing = System.Xml.DtdProcessing.Prohibit, XmlResolver = null };
                        using (var reader = System.Xml.XmlReader.Create(new System.IO.StringReader(PrependFormatData[i]), settings))
                        {
                            doc.Load(reader);

src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-TypeData.cs:1030

  • AppendFormatData has the same issue as PrependFormatData: invalid inline XML will throw XmlException directly (not caught by the existing RuntimeException catch), bypassing the normal FormatXmlUpdateException error path and leaving InitialSessionState.Formats mid-update. Catch XmlException and rethrow as PSInvalidOperationException so the existing revert-and-WriteError logic runs.
                        var doc = new System.Xml.XmlDocument() { XmlResolver = null };
                        var settings = new System.Xml.XmlReaderSettings { DtdProcessing = System.Xml.DtdProcessing.Prohibit, XmlResolver = null };
                        using (var reader = System.Xml.XmlReader.Create(new System.IO.StringReader(appendFormatDataItem), settings))
                        {
                            doc.Load(reader);

src/System.Management.Automation/utils/FormatAndTypeDataHelper.cs:52

  • The new PSSnapInTypeAndFormatErrors overload uses a boolean parameter only to disambiguate the (string, string) overloads, but the parameter is otherwise meaningless and is only referenced via _ = isXml;. Rename the parameter to _ and remove the dummy assignment to make the intent explicit and avoid the no-op statement.
        internal PSSnapInTypeAndFormatErrors(string psSnapinName, string formatDataXml, bool isXml)
        {
            this.psSnapinName = psSnapinName;
            FormatDataXml = formatDataXml;
            _ = isXml;

test/powershell/Modules/Microsoft.PowerShell.Utility/Update-FormatData.Tests.ps1:102

  • The new inline XML feature is only tested for the success path. Add a negative test to verify that invalid inline XML produces the same error surface as invalid file-based format XML (i.e., a FormatXmlUpdateException error record), so regressions like uncaught XmlException are caught.
        It "Should load inline XML using AppendFormatData" {
            $null = $ps.AddScript('param($xml) Update-FormatData -AppendFormatData $xml').AddArgument($xmlContent)
            $ps.Invoke()
            $ps.HadErrors | Should -BeFalse
            $ps.Commands.Clear()

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 6 out of 6 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (4)

src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-TypeData.cs:985

  • Inline XML is pre-parsed via XmlReaderSettings (DtdProcessing.Prohibit) and XmlDocument.Load without handling XmlException. This can (1) make -PrependFormatData reject XML that would load via the existing hardened loader settings and (2) surface as an unhandled terminating XmlException instead of a cmdlet error.
                        var doc = new System.Xml.XmlDocument() { XmlResolver = null };
                        var settings = new System.Xml.XmlReaderSettings { DtdProcessing = System.Xml.DtdProcessing.Prohibit, XmlResolver = null };
                        using (var reader = System.Xml.XmlReader.Create(new System.IO.StringReader(PrependFormatData[i]), settings))
                        {
                            doc.Load(reader);

src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-TypeData.cs:1030

  • Inline XML is parsed with XmlDocument.Load without bounds from the shared hardened settings, and XmlException will currently terminate the cmdlet unexpectedly. Aligning parsing with InternalDeserializer.LoadUnsafeXmlDocument also keeps behavior consistent with file-based format data loading.
                        var doc = new System.Xml.XmlDocument() { XmlResolver = null };
                        var settings = new System.Xml.XmlReaderSettings { DtdProcessing = System.Xml.DtdProcessing.Prohibit, XmlResolver = null };
                        using (var reader = System.Xml.XmlReader.Create(new System.IO.StringReader(appendFormatDataItem), settings))
                        {
                            doc.Load(reader);

src/System.Management.Automation/utils/FormatAndTypeDataHelper.cs:52

  • The new PSSnapInTypeAndFormatErrors constructor takes a bool that is never used ("_ = isXml;"). Since the bool exists only to disambiguate the (string,string) overload, consider naming it "_" and removing the dead assignment for clarity.
        internal PSSnapInTypeAndFormatErrors(string psSnapinName, string formatDataXml, bool isXml)
        {
            this.psSnapinName = psSnapinName;
            FormatDataXml = formatDataXml;
            _ = isXml;

src/System.Management.Automation/engine/InitialSessionState.cs:411

  • SessionStateFormatEntry now carries FormatDataXml, but InitialSessionState format loading doesn’t handle FormatDataXml entries (it only handles Formattable / FormatData / FileName). A SessionStateFormatEntry created with the XmlDocument constructor will likely be treated like a file entry with a null FileName when creating a new runspace, causing load failures.
        /// <summary>
        /// The FormatData XML string specified with constructor.
        /// This can be null if other constructors are used.
        /// </summary>
        public string FormatDataXml { get; }

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 6 out of 6 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (4)

src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-TypeData.cs:985

  • Update-FormatData -PrependFormatData parses the XML with XmlDocument.Load(...), but any XmlException (malformed XML) will currently escape ProcessRecord() unhandled. That produces an inconsistent error type/ID compared to file-based failures (which surface as FormatXmlUpdateException) and can bypass the cmdlet’s normal error-reporting behavior.
                        var doc = new System.Xml.XmlDocument() { XmlResolver = null };
                        var settings = new System.Xml.XmlReaderSettings { DtdProcessing = System.Xml.DtdProcessing.Prohibit, XmlResolver = null };
                        using (var reader = System.Xml.XmlReader.Create(new System.IO.StringReader(PrependFormatData[i]), settings))
                        {
                            doc.Load(reader);

src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-TypeData.cs:1030

  • Update-FormatData -AppendFormatData has the same issue as the prepend loop: malformed XML throws XmlException from XmlDocument.Load(...) and bypasses the cmdlet’s normal FormatXmlUpdateException error reporting.
                        var doc = new System.Xml.XmlDocument() { XmlResolver = null };
                        var settings = new System.Xml.XmlReaderSettings { DtdProcessing = System.Xml.DtdProcessing.Prohibit, XmlResolver = null };
                        using (var reader = System.Xml.XmlReader.Create(new System.IO.StringReader(appendFormatDataItem), settings))
                        {
                            doc.Load(reader);

src/System.Management.Automation/utils/FormatAndTypeDataHelper.cs:54

  • The isXml parameter name implies it affects behavior, but it’s intentionally unused (the _ = isXml; line is just a discard). Renaming the parameter to _ makes the intent explicit and avoids suggesting a behavior switch that doesn’t exist.
        internal PSSnapInTypeAndFormatErrors(string psSnapinName, string formatDataXml, bool isXml)
        {
            this.psSnapinName = psSnapinName;
            FormatDataXml = formatDataXml;
            _ = isXml;
            Errors = new ConcurrentBag<string>();
        }

test/powershell/Modules/Microsoft.PowerShell.Utility/Update-FormatData.Tests.ps1:102

  • Inline XML support is only tested for successful loads. There’s no test coverage for malformed inline XML (well-formedness errors) to ensure the cmdlet reports the expected FormatXmlUpdateException error ID (matching file-based behavior) instead of surfacing a raw XmlException.
        It "Should load inline XML using AppendFormatData" {
            $null = $ps.AddScript('param($xml) Update-FormatData -AppendFormatData $xml').AddArgument($xmlContent)
            $ps.Invoke()
            $ps.HadErrors | Should -BeFalse
            $ps.Commands.Clear()

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.

Supported way to load ExtendedTypeDefinition

2 participants