From c8817fe3b1039fce37e9c87bc59906fa443c6b61 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 16 Sep 2026 21:11:45 +0000 Subject: [PATCH] Fix literal path handling for inventory, SNMP cred maps, and DrawIO export Use -LiteralPath for Test-Path, Get-Content, and Out-File where user-supplied paths may contain wildcard metacharacters. Add Pester coverage for bracket paths and XML-special characters in diagram output. Co-authored-by: Jacobrakai --- CHANGELOG.md | 6 ++++ NetDiagram-PS/Public/Compare-NetworkScans.ps1 | 6 ++-- NetDiagram-PS/Public/Export-DrawIO.ps1 | 2 +- .../Public/Get-SnmpBridgeNeighbors.ps1 | 4 +-- NetDiagram-PS/Public/Get-SnmpNeighbors.ps1 | 4 +-- NetDiagram-PS/Public/Import-Inventory.ps1 | 4 +-- tests/NetDiagram.Tests.ps1 | 33 +++++++++++++++++++ 7 files changed, 49 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dc440c..1620681 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed +- **Literal path handling for JSON inputs and diagram output.** `Import-Inventory`, + SNMP credential-map readers, `Compare-NetworkScans` metadata inputs, and + `Export-DrawIO` now use `-LiteralPath` so paths containing `[]` or other + wildcard metacharacters work on all platforms. + ## [1.4.0] - 2026-09-09 ### Added diff --git a/NetDiagram-PS/Public/Compare-NetworkScans.ps1 b/NetDiagram-PS/Public/Compare-NetworkScans.ps1 index 0df7387..da376c4 100644 --- a/NetDiagram-PS/Public/Compare-NetworkScans.ps1 +++ b/NetDiagram-PS/Public/Compare-NetworkScans.ps1 @@ -40,9 +40,9 @@ function Compare-NetworkScans { # Load files (topology via Import-Topology for validation) try { - $baseMeta = Get-Content -Path $BaselineMetadata -Raw | ConvertFrom-Json + $baseMeta = Get-Content -LiteralPath $BaselineMetadata -Raw | ConvertFrom-Json $baseTopoData = Import-Topology -Path $BaselineTopology - $currMeta = Get-Content -Path $CurrentMetadata -Raw | ConvertFrom-Json + $currMeta = Get-Content -LiteralPath $CurrentMetadata -Raw | ConvertFrom-Json $currTopoData = Import-Topology -Path $CurrentTopology } catch { @@ -195,7 +195,7 @@ function Compare-NetworkScans { if ($OutFile) { try { - $reportText | Out-File -FilePath $OutFile -Encoding utf8 -Force + $reportText | Out-File -LiteralPath $OutFile -Encoding utf8 -Force Write-Verbose "Comparison report written to $OutFile" } catch { diff --git a/NetDiagram-PS/Public/Export-DrawIO.ps1 b/NetDiagram-PS/Public/Export-DrawIO.ps1 index 33b3466..2f2f235 100644 --- a/NetDiagram-PS/Public/Export-DrawIO.ps1 +++ b/NetDiagram-PS/Public/Export-DrawIO.ps1 @@ -316,7 +316,7 @@ function Export-DrawIO { # Write to file try { - $xml.ToString() | Out-File -FilePath $OutFile -Encoding utf8 -Force + $xml.ToString() | Out-File -LiteralPath $OutFile -Encoding utf8 -Force Write-Verbose "Exported diagram to $OutFile" } catch { diff --git a/NetDiagram-PS/Public/Get-SnmpBridgeNeighbors.ps1 b/NetDiagram-PS/Public/Get-SnmpBridgeNeighbors.ps1 index a8ed515..5d358fc 100644 --- a/NetDiagram-PS/Public/Get-SnmpBridgeNeighbors.ps1 +++ b/NetDiagram-PS/Public/Get-SnmpBridgeNeighbors.ps1 @@ -52,12 +52,12 @@ function Get-SnmpBridgeNeighbors { throw "Invalid topology object" } - if (-not (Test-Path -Path $CredentialMapPath -PathType Leaf)) { + if (-not (Test-Path -LiteralPath $CredentialMapPath -PathType Leaf)) { throw "Credential map file not found: $CredentialMapPath" } try { - $credMap = Get-Content -Path $CredentialMapPath -Raw | ConvertFrom-Json + $credMap = Get-Content -LiteralPath $CredentialMapPath -Raw | ConvertFrom-Json } catch { throw "Failed to parse credential map JSON: $_" diff --git a/NetDiagram-PS/Public/Get-SnmpNeighbors.ps1 b/NetDiagram-PS/Public/Get-SnmpNeighbors.ps1 index 7d7adbd..6563f44 100644 --- a/NetDiagram-PS/Public/Get-SnmpNeighbors.ps1 +++ b/NetDiagram-PS/Public/Get-SnmpNeighbors.ps1 @@ -73,12 +73,12 @@ function Get-SnmpNeighbors { throw "Invalid topology object" } - if (-not (Test-Path -Path $CredentialMapPath -PathType Leaf)) { + if (-not (Test-Path -LiteralPath $CredentialMapPath -PathType Leaf)) { throw "Credential map file not found: $CredentialMapPath" } try { - $credMap = Get-Content -Path $CredentialMapPath -Raw | ConvertFrom-Json + $credMap = Get-Content -LiteralPath $CredentialMapPath -Raw | ConvertFrom-Json } catch { throw "Failed to parse credential map JSON: $_" diff --git a/NetDiagram-PS/Public/Import-Inventory.ps1 b/NetDiagram-PS/Public/Import-Inventory.ps1 index dcd5fd5..69a0b4e 100644 --- a/NetDiagram-PS/Public/Import-Inventory.ps1 +++ b/NetDiagram-PS/Public/Import-Inventory.ps1 @@ -20,12 +20,12 @@ function Import-Inventory { ) process { - if (-not (Test-Path -Path $Path -PathType Leaf)) { + if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { throw "Inventory file not found: $Path" } try { - $inventory = Get-Content -Path $Path -Raw | ConvertFrom-Json + $inventory = Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json } catch { throw "Failed to parse inventory JSON: $_" diff --git a/tests/NetDiagram.Tests.ps1 b/tests/NetDiagram.Tests.ps1 index 46b67a0..a51a150 100644 --- a/tests/NetDiagram.Tests.ps1 +++ b/tests/NetDiagram.Tests.ps1 @@ -99,6 +99,15 @@ Describe 'Import-Inventory' { { Import-Inventory -Path 'C:\NonExistent\file.json' } | Should -Throw } + It 'Reads inventory paths that contain wildcard metacharacters literally' { + $path = Join-Path $TestDrive 'inventory[bracket].json' + '{"knownDevices":[{"ip":"10.0.0.5","hostname":"bracket-host"}]}' | Set-Content -LiteralPath $path + + $topology = Import-Inventory -Path $path + $topology.Nodes | Should -HaveCount 1 + $topology.Nodes[0].IP | Should -Be '10.0.0.5' + } + It 'Accepts a valid empty inventory' { $path = Join-Path $TestDrive 'empty-inventory.json' '{"knownDevices":[],"subnets":[]}' | Set-Content -Path $path @@ -491,6 +500,30 @@ Describe 'Export-DrawIO' { # Try to load as XML to verify it's valid { [xml](Get-Content -Path $drawioPath -Raw) } | Should -Not -Throw } + + It 'Escapes XML metacharacters in node labels and attributes' { + $path = Join-Path $TestDrive 'xml-special.json' + @{ + knownDevices = @( + @{ + ip = '10.0.0.99' + hostname = 'host