From 188b61196a694ef8bedfcb2932b6a4232452d37f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 16:16:10 +0000 Subject: [PATCH 1/4] Initial plan From 6eea5385f0e0c72ea2eb64336ec31ab21368ab4b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 16:19:56 +0000 Subject: [PATCH 2/4] Fix: move bearerTokenPattern variables inside InvokeSafely scriptblock to fix variable-not-set error Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com> --- .../Private/New-IdleExchangeOnlineAdapter.ps1 | 8 ++-- .../ExchangeOnlineProvider.Tests.ps1 | 38 +++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/IdLE.Provider.ExchangeOnline/Private/New-IdleExchangeOnlineAdapter.ps1 b/src/IdLE.Provider.ExchangeOnline/Private/New-IdleExchangeOnlineAdapter.ps1 index 7013d6c2..470d579e 100644 --- a/src/IdLE.Provider.ExchangeOnline/Private/New-IdleExchangeOnlineAdapter.ps1 +++ b/src/IdLE.Provider.ExchangeOnline/Private/New-IdleExchangeOnlineAdapter.ps1 @@ -18,10 +18,6 @@ function New-IdleExchangeOnlineAdapter { [switch] $UseRestApi ) - # Regex patterns for sanitizing error messages (captured by scriptblock closure) - $bearerTokenPattern = 'Bearer\s+[^\s]+' - $tokenAssignmentPattern = 'token[^\s]*\s*=\s*[^\s,;]+' - $adapter = [pscustomobject]@{ PSTypeName = 'IdLE.ExchangeOnlineAdapter' UseRestApi = [bool]$UseRestApi @@ -37,6 +33,10 @@ function New-IdleExchangeOnlineAdapter { [hashtable] $Parameters = @{} ) + # Regex patterns for sanitizing error messages (defined inside scriptblock for reliable scoping) + $bearerTokenPattern = 'Bearer\s+[^\s]+' + $tokenAssignmentPattern = 'token[^\s]*\s*=\s*[^\s,;]+' + try { $result = & $CommandName @Parameters return $result diff --git a/tests/Providers/ExchangeOnlineProvider.Tests.ps1 b/tests/Providers/ExchangeOnlineProvider.Tests.ps1 index a033492c..4082b071 100644 --- a/tests/Providers/ExchangeOnlineProvider.Tests.ps1 +++ b/tests/Providers/ExchangeOnlineProvider.Tests.ps1 @@ -445,6 +445,44 @@ Describe 'ExchangeOnline provider - Unit tests' { } } + Context 'New-IdleExchangeOnlineAdapter - InvokeSafely scoping regression' { + BeforeAll { + # Import private adapter function directly for unit testing + $repoRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent + $adapterPath = Join-Path -Path $repoRoot -ChildPath 'src\IdLE.Provider.ExchangeOnline\Private\New-IdleExchangeOnlineAdapter.ps1' + . $adapterPath + } + + It 'InvokeSafely can be called from another ScriptMethod without variable-not-set error' { + # Regression test: $bearerTokenPattern and $tokenAssignmentPattern must be in scope + # when InvokeSafely is invoked via $this.InvokeSafely() from another ScriptMethod. + $adapter = New-IdleExchangeOnlineAdapter + + # Wrap the real adapter with a ScriptMethod that calls $this.InvokeSafely() to + # simulate the same execution path as GetMailbox -> InvokeSafely. + $adapter | Add-Member -MemberType ScriptMethod -Name TestViaMethod -Value { + $this.InvokeSafely('Write-Output', @{ InputObject = 'ok' }) + } -Force + + { $adapter.TestViaMethod() } | Should -Not -Throw + } + + It 'InvokeSafely sanitizes bearer tokens in error messages without variable-not-set error' { + $adapter = New-IdleExchangeOnlineAdapter + + # Simulate a command that throws an exception containing a bearer token in the message. + function global:Invoke-ThrowBearerError { + throw 'Authentication failed: Bearer eyJhbGciOiJSUzI1NiJ9.payload.sig' + } + + $adapter | Add-Member -MemberType ScriptMethod -Name TestErrorSanitization -Value { + $this.InvokeSafely('Invoke-ThrowBearerError', @{}) + } -Force + + { $adapter.TestErrorSanitization() } | Should -Throw -ExpectedMessage "*Bearer *" + } + } + Context 'Normalize-IdleExchangeOnlineAutoReplyMessage' { BeforeAll { # Import the private normalization function for direct testing From 42edb0c0d7b49a8fc5afbac4e66a2236850496a5 Mon Sep 17 00:00:00 2001 From: Matthias <13959569+blindzero@users.noreply.github.com> Date: Mon, 23 Feb 2026 17:54:47 +0100 Subject: [PATCH 3/4] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/Providers/ExchangeOnlineProvider.Tests.ps1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/Providers/ExchangeOnlineProvider.Tests.ps1 b/tests/Providers/ExchangeOnlineProvider.Tests.ps1 index 4082b071..f835b2fd 100644 --- a/tests/Providers/ExchangeOnlineProvider.Tests.ps1 +++ b/tests/Providers/ExchangeOnlineProvider.Tests.ps1 @@ -450,6 +450,10 @@ Describe 'ExchangeOnline provider - Unit tests' { # Import private adapter function directly for unit testing $repoRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent $adapterPath = Join-Path -Path $repoRoot -ChildPath 'src\IdLE.Provider.ExchangeOnline\Private\New-IdleExchangeOnlineAdapter.ps1' + + if (-not (Test-Path -LiteralPath $adapterPath)) { + throw "Private adapter function file not found at path: $adapterPath" + } . $adapterPath } From d86da8d12f2c0c424ce03ec369c764b3d8eb8cd3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 17:00:06 +0000 Subject: [PATCH 4/4] Fix: replace global:Invoke-ThrowBearerError with dedicated _testHelpers.Providers.ps1 helper Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com> --- .../Providers/ExchangeOnlineProvider.Tests.ps1 | 10 ++++------ tests/Providers/_testHelpers.Providers.ps1 | 17 +++++++++++++++-- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/tests/Providers/ExchangeOnlineProvider.Tests.ps1 b/tests/Providers/ExchangeOnlineProvider.Tests.ps1 index f835b2fd..24447515 100644 --- a/tests/Providers/ExchangeOnlineProvider.Tests.ps1 +++ b/tests/Providers/ExchangeOnlineProvider.Tests.ps1 @@ -455,6 +455,9 @@ Describe 'ExchangeOnline provider - Unit tests' { throw "Private adapter function file not found at path: $adapterPath" } . $adapterPath + + # Dot-source provider test helpers so Invoke-IdleTestBearerTokenError is available at run time + . (Join-Path -Path $PSScriptRoot -ChildPath '_testHelpers.Providers.ps1') } It 'InvokeSafely can be called from another ScriptMethod without variable-not-set error' { @@ -474,13 +477,8 @@ Describe 'ExchangeOnline provider - Unit tests' { It 'InvokeSafely sanitizes bearer tokens in error messages without variable-not-set error' { $adapter = New-IdleExchangeOnlineAdapter - # Simulate a command that throws an exception containing a bearer token in the message. - function global:Invoke-ThrowBearerError { - throw 'Authentication failed: Bearer eyJhbGciOiJSUzI1NiJ9.payload.sig' - } - $adapter | Add-Member -MemberType ScriptMethod -Name TestErrorSanitization -Value { - $this.InvokeSafely('Invoke-ThrowBearerError', @{}) + $this.InvokeSafely('Invoke-IdleTestBearerTokenError', @{}) } -Force { $adapter.TestErrorSanitization() } | Should -Throw -ExpectedMessage "*Bearer *" diff --git a/tests/Providers/_testHelpers.Providers.ps1 b/tests/Providers/_testHelpers.Providers.ps1 index 58fe7df3..c99077a9 100644 --- a/tests/Providers/_testHelpers.Providers.ps1 +++ b/tests/Providers/_testHelpers.Providers.ps1 @@ -13,5 +13,18 @@ directly by test files. #> # Provider-specific helpers will be added here as needed. -# This file is currently empty but provides a clear extension point for -# provider-related test infrastructure. + +function Invoke-IdleTestBearerTokenError { + <# + .SYNOPSIS + Test helper: throws an exception whose message contains a bearer token. + + .DESCRIPTION + Used by adapter unit tests to verify that InvokeSafely correctly sanitizes + bearer tokens from error messages without leaking sensitive data. + #> + [CmdletBinding()] + param() + + throw 'Authentication failed: Bearer eyJhbGciOiJSUzI1NiJ9.payload.sig' +}