diff --git a/.github/ci-scripts/Actions_Bootstrap.ps1 b/.github/ci-scripts/Actions_Bootstrap.ps1 index 0a367098..bdf75697 100644 --- a/.github/ci-scripts/Actions_Bootstrap.ps1 +++ b/.github/ci-scripts/Actions_Bootstrap.ps1 @@ -9,6 +9,10 @@ .EXAMPLE ./.github/scripts/Actions_Bootstrap.ps1 +.PARAMETER ModuleInstallPath + Optional isolated module root. Exact tool versions are saved here and the path is + prepended only to this process's PSModulePath. No user-scope module path is mutated. + .NOTES Run this script at the beginning of CI/CD workflows to ensure all dependencies are available. #> @@ -16,76 +20,82 @@ [CmdletBinding()] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '')] -param() +param( + [Parameter()] + [string]$ModuleInstallPath +) $ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' Write-Host '🔨 Bootstrapping CI/CD Environment...' +$RepositoryRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent +$ToolingScriptPath = Join-Path -Path $RepositoryRoot -ChildPath 'build/DLLPickle.Tooling.ps1' +$ToolPolicyPath = Join-Path -Path $RepositoryRoot -ChildPath 'build/build-tool-versions.json' +. $ToolingScriptPath +$ToolPolicy = Get-DLLPickleBuildToolPolicy -Path $ToolPolicyPath + +if (-not [string]::IsNullOrWhiteSpace($ModuleInstallPath)) { + $ModuleInstallPath = [System.IO.Path]::GetFullPath($ModuleInstallPath) + if (-not (Test-Path -LiteralPath $ModuleInstallPath -PathType Container)) { + $null = New-Item -Path $ModuleInstallPath -ItemType Directory -Force + } + $ExistingModulePathEntries = @($env:PSModulePath -split [System.IO.Path]::PathSeparator | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) + $env:PSModulePath = @($ModuleInstallPath) + $ExistingModulePathEntries -join [System.IO.Path]::PathSeparator +} + # https://docs.microsoft.com/powershell/module/packagemanagement/get-packageprovider Get-PackageProvider -Name Nuget -ForceBootstrap | Out-Null # https://docs.microsoft.com/powershell/module/powershellget/set-psrepository Set-PSRepository -Name PSGallery -InstallationPolicy Trusted -# List of PowerShell Modules required for the build. -$ModulesToInstall = New-Object System.Collections.Generic.List[object] - -# https://github.com/pester/Pester -$ModulesToInstall.Add(([PSCustomObject]@{ - ModuleName = 'Pester' - SkipPublisherCheck = $true # Skip publisher check for older Pester versions due to certificate mismatch. - #ModuleVersion = '5.7.1' - })) | Out-Null - -# https://github.com/nightroman/Invoke-Build -$ModulesToInstall.Add(([PSCustomObject]@{ - ModuleName = 'InvokeBuild' - #ModuleVersion = '5.12.1' - })) | Out-Null - -# https://github.com/PowerShell/PSScriptAnalyzer -$ModulesToInstall.Add(([PSCustomObject]@{ - ModuleName = 'PSScriptAnalyzer' - #ModuleVersion = '1.23.0' - })) | Out-Null - -# https://github.com/PowerShell/Microsoft.PowerShell.PlatyPS -$ModulesToInstall.Add(([PSCustomObject]@{ - ModuleName = 'Microsoft.PowerShell.PlatyPS' - })) | Out-Null -# https://github.com/PowerShell/platyPS -# Older version used due to: https://github.com/PowerShell/platyPS/issues/457 -#$ModulesToInstall.Add(([PSCustomObject]@{ -# ModuleName = 'platyPS' -# #ModuleVersion = '0.12.0' -#})) | Out-Null - -Write-Host '📦 Installing PowerShell Modules' -foreach ($Module in $ModulesToInstall) { - $InstallSplat = @{ - Name = $Module.ModuleName - Repository = 'PSGallery' - Force = $true - ErrorAction = 'Stop' - } - if ($Module.ModuleVersion) { - $InstallSplat['RequiredVersion'] = $Module.ModuleVersion - } - if ($Module.SkipPublisherCheck) { - $InstallSplat['SkipPublisherCheck'] = $true +Write-Host '📦 Installing exact PowerShell build-tool versions' +$ModuleInstallCommandName = if ([string]::IsNullOrWhiteSpace($ModuleInstallPath)) { + 'Install-Module' +} else { + 'Save-Module' +} +$ModuleInstallCommand = Get-Command -Name $ModuleInstallCommandName -ErrorAction Stop + +foreach ($Module in @($ToolPolicy.modules)) { + $RequiredVersion = [version]$Module.version + $InstalledModule = Get-Module -ListAvailable -Name $Module.name | + Where-Object { Test-DLLPickleToolVersionMatch -ActualVersion $_.Version -RequiredVersion $RequiredVersion } | + Select-Object -First 1 + + if (-not $InstalledModule) { + $ModuleCommandSplat = @{ + Name = $Module.name + RequiredVersion = $Module.version + Repository = 'PSGallery' + Force = $true + ErrorAction = 'Stop' + } + if ( + $Module.skipPublisherCheck -and + (Test-DLLPickleCommandParameter -Command $ModuleInstallCommand -ParameterName 'SkipPublisherCheck') + ) { + $ModuleCommandSplat['SkipPublisherCheck'] = $true + } + + try { + if ([string]::IsNullOrWhiteSpace($ModuleInstallPath)) { + $ModuleCommandSplat['Scope'] = 'CurrentUser' + & $ModuleInstallCommand @ModuleCommandSplat + } else { + $ModuleCommandSplat['Path'] = $ModuleInstallPath + & $ModuleInstallCommand @ModuleCommandSplat + } + } catch { + Write-Host " - Failed to install $($Module.name) $RequiredVersion" + throw + } } - try { - Install-Module @InstallSplat - Import-Module -Name $Module.ModuleName -ErrorAction Stop - Write-Host " - Successfully installed $($Module.ModuleName)" - } catch { - $message = 'Failed to install {0}' -f $Module.ModuleName - Write-Host " - $message" - throw - } + $ImportedModule = Import-DLLPickleBuildTool -Name $Module.name -RequiredVersion $RequiredVersion + Write-Host " - $($ImportedModule.Name) $($ImportedModule.Version) ready" } # Ensure .NET tools are available diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 75e17ba9..5a62641a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -25,7 +25,7 @@ updates: interval: "weekly" day: "sunday" groups: - all-actions: + ci-tooling-actions: patterns: ["*"] commit-message: prefix: "ci" @@ -36,15 +36,25 @@ updates: schedule: interval: "daily" groups: - nuget-minor-patch: - patterns: ["*"] + runtime-bundle-minor-patch: + patterns: + - "Microsoft.Identity.Client*" + - "Microsoft.IdentityModel.*" + - "System.IdentityModel.Tokens.Jwt" + - "System.Security.Cryptography.ProtectedData" + - "Microsoft.Extensions.DependencyInjection.Abstractions" + - "Microsoft.Extensions.Logging.Abstractions" update-types: - "minor" - "patch" commit-message: prefix: "deps" prefix-development: "deps" - # Note: Major version PRs are created but require manual approval. The Dependabot-Auto-Approve workflow handles selective auto-approval: + # Runtime dependencies are intentionally separate from GitHub Actions and the + # exact PowerShell build-tool pins in build/build-tool-versions.json. The + # optional pre-1.0 multi-pwsh pin is CI provisioning policy and always requires + # reviewed test-matrix evidence; it is not a NuGet dependency. + # Major version PRs are created but require manual approval. The Dependabot-Auto-Approve workflow handles selective auto-approval: # - Auto-approves: patch and minor updates # - Requires manual review: major version updates diff --git a/.github/workflows/Build Module.yml b/.github/workflows/Build Module.yml index 48dcaae8..c41da7da 100644 --- a/.github/workflows/Build Module.yml +++ b/.github/workflows/Build Module.yml @@ -76,7 +76,17 @@ jobs: # docs/gaps/ is build-relevant: the gap-register guard (tests/Unit/GapRegister.Tests.ps1) # must run when gap files or their index change, otherwise a docs-only gap edit would skip # the very check that detects gap-register drift (GAP-011). - $Patterns = @('^src/', '^build/', '^tests/', '^tools/', '^docs/gaps/', '^\.github/ci-scripts/', '^\.github/workflows/Build Module\.yml$') + $Patterns = @( + '^src/' + '^build/' + '^tests/' + '^tools/' + '^docs/gaps/' + '^\.github/ci-scripts/' + '^\.github/workflows/Build Module\.yml$' + '^global\.json$' + '^build/powershell-test-matrix\.json$' + ) $Relevant = $false foreach ($File in $Files) { foreach ($Pattern in $Patterns) { @@ -91,6 +101,29 @@ jobs: "relevant=$($Relevant.ToString().ToLowerInvariant())" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8 Write-Host "Build-relevant change detected: $Relevant" + runtime-matrix: + name: Generate exact PowerShell runtime matrix + needs: changes + if: ${{ always() && (inputs.ref != '' || github.event_name == 'workflow_dispatch' || needs.changes.outputs.relevant != 'false') }} + runs-on: ubuntu-24.04 + permissions: + contents: read + outputs: + matrix: ${{ steps.generate.outputs.matrix }} + steps: + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ inputs.ref || github.ref }} + persist-credentials: false + + - name: Generate matrix from support policy + id: generate + shell: pwsh + run: | + $Matrix = ./tools/New-DLLPicklePowerShellTestMatrix.ps1 -Compress + "matrix=$Matrix" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8 + build: name: Build and test module (${{ matrix.os }}) @@ -122,13 +155,13 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ inputs.ref || github.ref }} + persist-credentials: false - # Install the .NET 8 SDK pinned by global.json so the build does not depend on the runner image - # having an 8.0.x SDK preinstalled (Invoke-Build's RestoreDependencies task invokes dotnet). + # Install the exact .NET SDK pinned by global.json so the build does not depend on the runner image. - name: Setup .NET uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: - dotnet-version: "8.0.x" + global-json-file: global.json cache: true cache-dependency-path: "src/DLLPickle.Build/packages.lock.json" @@ -136,9 +169,9 @@ jobs: uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ${{ matrix.module_cache_paths }} - key: ${{ runner.os }}-psmodules-${{ hashFiles('build/DLLPickle.Build.ps1', '.github/ci-scripts/Actions_Bootstrap.ps1') }} + key: ${{ runner.os }}-${{ runner.arch }}-pwsh-${{ hashFiles('build/build-tool-versions.json') }}-psmodules-${{ hashFiles('build/DLLPickle.Build.ps1', 'build/DLLPickle.Tooling.ps1', '.github/ci-scripts/Actions_Bootstrap.ps1') }} restore-keys: | - ${{ runner.os }}-psmodules- + ${{ runner.os }}-${{ runner.arch }}-pwsh-${{ hashFiles('build/build-tool-versions.json') }}-psmodules- - name: Bootstrap shell: pwsh @@ -147,17 +180,47 @@ jobs: - name: Test and Build (Windows) if: runner.os == 'Windows' shell: pwsh - run: Invoke-Build -File ./build/DLLPickle.Build.ps1 + run: ./tools/Invoke-DLLPickleBuild.ps1 - name: Test and Build (Linux/macOS) if: runner.os != 'Windows' shell: pwsh - run: Invoke-Build -File ./build/DLLPickle.Build.ps1 -Task BuildCrossPlatform + run: ./tools/Invoke-DLLPickleBuild.ps1 -Task BuildCrossPlatform + + - name: Verify package composition and size policy + if: runner.os == 'Windows' + shell: pwsh + run: | + $CompositionParameters = @{ + OutputPath = './artifacts/package/artifact-composition.json' + Strict = $true + } + $Composition = ./tools/Test-DLLPicklePackageArtifact.ps1 @CompositionParameters + + $SizeParameters = @{ + OutputPath = './artifacts/package/artifact-size.json' + Strict = $true + } + $Size = ./tools/New-DLLPickleArtifactSizeReport.ps1 @SizeParameters + + @( + '### Package policy' + '' + "- Target frameworks: $($Composition.ActualTargetFrameworks -join ', ')" + "- Artifact composition: $($Composition.Passed)" + "- Material size review required: $($Size.ReviewRequired)" + "- Full unpacked bytes: $($Size.FullArtifact.UnpackedBytes)" + "- Full deterministic compressed bytes: $($Size.FullArtifact.CompressedBytes)" + ) | Add-Content -LiteralPath $env:GITHUB_STEP_SUMMARY - name: Generate PSScriptAnalyzer SARIF if: always() && runner.os == 'Windows' shell: pwsh run: | + . ./build/DLLPickle.Tooling.ps1 + $ToolPolicy = Get-DLLPickleBuildToolPolicy + $AnalyzerVersion = Get-DLLPickleBuildToolVersion -Policy $ToolPolicy -Name 'PSScriptAnalyzer' + $null = Import-DLLPickleBuildTool -Name 'PSScriptAnalyzer' -RequiredVersion $AnalyzerVersion $null = New-Item -Path ./artifacts -ItemType Directory -Force $Results = @(Invoke-ScriptAnalyzer -Path ./src/DLLPickle -Settings ./build/PSScriptAnalyzerSettings.psd1 -Recurse) @@ -195,7 +258,7 @@ jobs: runs = @([ordered]@{ tool = @{ driver = @{ name = 'PSScriptAnalyzer' - version = (Get-Module PSScriptAnalyzer -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1).Version.ToString() + version = (Get-Module PSScriptAnalyzer).Version.ToString() rules = $Rules }} results = $SarifResults @@ -219,6 +282,14 @@ jobs: path: ./artifacts/testOutput if-no-files-found: warn + - name: Upload package policy reports + if: always() && runner.os == 'Windows' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: package-policy-reports + path: ./artifacts/package + if-no-files-found: warn + - name: Upload zip module archive build if: runner.os == 'Windows' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 @@ -235,6 +306,217 @@ jobs: path: ./module/DLLPickle if-no-files-found: warn + runtime-tests: + name: PowerShell ${{ matrix.powerShellVersion }} on ${{ matrix.platform }} + needs: runtime-matrix + if: ${{ needs.runtime-matrix.result == 'success' }} + runs-on: ${{ matrix.runner }} + timeout-minutes: 30 + permissions: + contents: read + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.runtime-matrix.outputs.matrix) }} + steps: + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ inputs.ref || github.ref }} + persist-credentials: false + + - name: Setup .NET + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 + with: + global-json-file: global.json + cache: true + cache-dependency-path: "src/DLLPickle.Build/packages.lock.json" + + - name: Cache exact stock PowerShell archive and payload + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ${{ runner.temp }}/dllpickle-test-powershell + key: ${{ runner.os }}-${{ runner.arch }}-${{ matrix.provider }}-${{ matrix.powerShellVersion }}-${{ hashFiles('build/powershell-test-matrix.json', 'tools/Install-DLLPickleTestPowerShell.ps1') }} + + - name: Cache isolated PowerShell build modules + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ${{ runner.temp }}/dllpickle-psmodules/${{ matrix.powerShellVersion }} + key: ${{ runner.os }}-${{ runner.arch }}-pwsh-${{ matrix.powerShellVersion }}-tools-${{ hashFiles('build/build-tool-versions.json', 'build/DLLPickle.Tooling.ps1', '.github/ci-scripts/Actions_Bootstrap.ps1') }} + + - name: Provision and verify exact stock PowerShell + id: runtime + shell: pwsh + env: + MATRIX_PROVIDER: ${{ matrix.provider }} + MATRIX_POWERSHELL_VERSION: ${{ matrix.powerShellVersion }} + MATRIX_PLATFORM: ${{ matrix.platform }} + MATRIX_ARCHITECTURE: ${{ matrix.architecture }} + RUNTIME_INSTALL_ROOT: ${{ runner.temp }}/dllpickle-test-powershell + run: | + $InstallParameters = @{ + Provider = $env:MATRIX_PROVIDER + PowerShellVersion = $env:MATRIX_POWERSHELL_VERSION + Platform = $env:MATRIX_PLATFORM + Architecture = $env:MATRIX_ARCHITECTURE + InstallRoot = $env:RUNTIME_INSTALL_ROOT + PassThru = $true + } + $Identity = ./tools/Install-DLLPickleTestPowerShell.ps1 @InstallParameters + "DLLPICKLE_TEST_PWSH=$($Identity.ExecutablePath)" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 + "DLLPICKLE_TEST_TFM=$($Identity.TargetFramework)" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 + $Identity | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath ./runtime-identity.json -Encoding utf8 + + - name: Bootstrap exact tools in an isolated module path + shell: pwsh + env: + CELL_MODULE_ROOT: ${{ runner.temp }}/dllpickle-psmodules/${{ matrix.powerShellVersion }} + run: | + $CellModuleRoot = $env:CELL_MODULE_ROOT + $SystemModuleRoot = Join-Path -Path (Split-Path -Path $env:DLLPICKLE_TEST_PWSH -Parent) -ChildPath 'Modules' + $IsolatedModulePath = @($CellModuleRoot, $SystemModuleRoot) -join [System.IO.Path]::PathSeparator + "DLLPICKLE_TEST_PSMODULEPATH=$IsolatedModulePath" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 + $env:PSModulePath = $IsolatedModulePath + & $env:DLLPICKLE_TEST_PWSH -NoLogo -NoProfile -NonInteractive -File ./.github/ci-scripts/Actions_Bootstrap.ps1 -ModuleInstallPath $CellModuleRoot + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + + - name: Run unit tests in a fresh exact-runtime process + shell: pwsh + run: | + $env:PSModulePath = $env:DLLPICKLE_TEST_PSMODULEPATH + & $env:DLLPICKLE_TEST_PWSH -NoLogo -NoProfile -NonInteractive -File ./tools/Invoke-DLLPickleBuild.ps1 -Task Test + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + + - name: Build, package, and run integration regressions in a fresh exact-runtime process + shell: pwsh + run: | + $env:PSModulePath = $env:DLLPICKLE_TEST_PSMODULEPATH + & $env:DLLPICKLE_TEST_PWSH -NoLogo -NoProfile -NonInteractive -File ./tools/Invoke-DLLPickleBuild.ps1 -Task IssueReproTest + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + + - name: Inspect the exact-runtime package and resolved TFM assets + shell: pwsh + run: | + $env:PSModulePath = $env:DLLPICKLE_TEST_PSMODULEPATH + & $env:DLLPICKLE_TEST_PWSH -NoLogo -NoProfile -NonInteractive -File ./tools/Test-DLLPicklePackageArtifact.ps1 -Strict + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + & $env:DLLPICKLE_TEST_PWSH -NoLogo -NoProfile -NonInteractive -File ./tools/Test-DLLPickleTfmAlignment.ps1 -Strict + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + + - name: Capture selected bundle and assembly load-context evidence + if: ${{ always() && steps.runtime.outcome == 'success' }} + shell: pwsh + env: + MATRIX_POWERSHELL_VERSION: ${{ matrix.powerShellVersion }} + MATRIX_TARGET_FRAMEWORK: ${{ matrix.targetFramework }} + RUNTIME_EVIDENCE_PATH: ./artifacts/runtime-evidence-${{ matrix.powerShellVersion }}-${{ matrix.platform }}.json + run: | + $EvidenceParameters = @{ + PowerShellExecutable = $env:DLLPICKLE_TEST_PWSH + PowerShellVersion = $env:MATRIX_POWERSHELL_VERSION + TargetFramework = $env:MATRIX_TARGET_FRAMEWORK + ModuleManifestPath = './module/DLLPickle/DLLPickle.psd1' + OutputPath = $env:RUNTIME_EVIDENCE_PATH + } + ./tools/New-DLLPickleRuntimeProfileEvidence.ps1 @EvidenceParameters + + - name: Upload structured runtime result + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: runtime-${{ matrix.powerShellVersion }}-${{ matrix.platform }}-${{ matrix.architecture }} + path: | + runtime-identity.json + artifacts/runtime-evidence-*.json + artifacts/testOutput/*.xml + if-no-files-found: warn + retention-days: 14 + + dependency-change-report: + name: Per-TFM dependency change report + needs: [build, runtime-tests] + if: >- + github.event_name == 'pull_request' && + github.actor == 'dependabot[bot]' && + github.event.pull_request.user.login == 'dependabot[bot]' && + needs.build.result == 'success' && + needs.runtime-tests.result == 'success' + runs-on: ubuntu-latest + timeout-minutes: 20 + permissions: + contents: read + + steps: + - name: Check out candidate + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Check out pull request base + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event.pull_request.base.sha }} + path: dependency-baseline + persist-credentials: false + + - name: Setup .NET + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 + with: + global-json-file: global.json + + - name: Restore and build baseline and candidate graphs + shell: pwsh + run: | + $BaselineGlobalJson = './dependency-baseline/global.json' + if (Test-Path -LiteralPath $BaselineGlobalJson -PathType Leaf) { + Remove-Item -LiteralPath $BaselineGlobalJson -Force + } + $Projects = @( + './dependency-baseline/src/DLLPickle.Build/DLLPickle.csproj' + './src/DLLPickle.Build/DLLPickle.csproj' + ) + foreach ($Project in $Projects) { + dotnet restore $Project --locked-mode + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + dotnet build $Project --configuration Release --no-restore + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + } + + - name: Download package size evidence + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: package-policy-reports + path: downloaded-package-policy + + - name: Download exact-runtime scenario evidence + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + pattern: runtime-* + path: downloaded-runtime-evidence + + - name: Generate per-TFM major-update evidence + shell: pwsh + run: | + $Parameters = @{ + BaselineProjectAssetsPath = './dependency-baseline/src/DLLPickle.Build/obj/project.assets.json' + CandidateProjectAssetsPath = './src/DLLPickle.Build/obj/project.assets.json' + BaselineBuildOutputRoot = './dependency-baseline/src/DLLPickle.Build/bin/Release' + CandidateBuildOutputRoot = './src/DLLPickle.Build/bin/Release' + SupportPolicyPath = './src/DLLPickle/SupportedRuntimeProfiles.json' + DependencyPolicyPath = './build/dependency-policy.json' + SizeReportPath = './downloaded-package-policy/artifact-size.json' + ScenarioEvidencePath = './downloaded-runtime-evidence' + OutputPath = './artifacts/dependency/dependency-change-report.json' + } + ./tools/New-DLLPickleDependencyChangeReport.ps1 @Parameters + + - name: Upload per-TFM dependency report + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: dependency-change-report + path: ./artifacts/dependency/dependency-change-report.json + if-no-files-found: error + retention-days: 30 + build-gate: name: Build gate # Aggregate required-check target. GitHub evaluates a matrix job's `if` BEFORE expanding the @@ -243,18 +525,48 @@ jobs: # for status". This single, always-running job is the stable check to require instead: it passes # when the matrix build succeeded OR was skipped (no build-relevant changes) and fails when the # build failed or was cancelled. - needs: build + needs: [changes, runtime-matrix, build, runtime-tests, dependency-change-report] if: ${{ always() }} runs-on: ubuntu-latest steps: - name: Aggregate build result shell: pwsh + env: + EVENT_NAME: ${{ github.event_name }} + INPUT_REF: ${{ inputs.ref }} + BUILD_RELEVANT: ${{ needs.changes.outputs.relevant }} run: | - $Result = '${{ needs.build.result }}' - Write-Host "build job result: $Result" - if ($Result -eq 'success' -or $Result -eq 'skipped') { + $Results = @{ + changes = '${{ needs.changes.result }}' + runtimeMatrix = '${{ needs.runtime-matrix.result }}' + build = '${{ needs.build.result }}' + runtimeTests = '${{ needs.runtime-tests.result }}' + dependencyChangeReport = '${{ needs.dependency-change-report.result }}' + } + $Results.GetEnumerator() | Sort-Object Name | ForEach-Object { + Write-Host "$($_.Name) job result: $($_.Value)" + } + $BuildRequired = -not [string]::IsNullOrWhiteSpace($env:INPUT_REF) -or + $env:EVENT_NAME -eq 'workflow_dispatch' -or + $env:BUILD_RELEVANT -ne 'false' + Write-Host "Exact runtime validation required: $BuildRequired" + + $Failures = [System.Collections.Generic.List[string]]::new() + foreach ($Result in $Results.GetEnumerator()) { + if ($Result.Value -notin @('success', 'skipped')) { + $Failures.Add($Result.Name) + } + } + if ($BuildRequired) { + foreach ($RequiredJob in @('runtimeMatrix', 'build', 'runtimeTests')) { + if ($Results[$RequiredJob] -ne 'success' -and -not $Failures.Contains($RequiredJob)) { + $Failures.Add($RequiredJob) + } + } + } + if ($Failures.Count -eq 0) { Write-Host 'Build gate passed.' exit 0 } - Write-Host "::error::Build gate failed (build result: $Result)." + Write-Host "::error::Build gate failed: $(@($Failures | Sort-Object -Unique) -join ', ')." exit 1 diff --git a/.github/workflows/Dependabot-Auto-Approve.yml b/.github/workflows/Dependabot-Auto-Approve.yml index 95d69088..7c3d98e2 100644 --- a/.github/workflows/Dependabot-Auto-Approve.yml +++ b/.github/workflows/Dependabot-Auto-Approve.yml @@ -1,4 +1,4 @@ -# Auto-approves and auto-merges Dependabot pull requests that update the NuGet packages in src/DLLPickle.Build (patch/minor only). +# Auto-approves and registers auto-merge for Dependabot pull requests that update the NuGet runtime bundle in src/DLLPickle.Build (patch/minor only). # Major updates are converted to a draft PR with detailed review notes (decision 4 / docs/Architecture.md section 8.2) -- never auto-merged or auto-published. # # Requirements: @@ -76,6 +76,14 @@ jobs: permission-contents: write permission-pull-requests: write + - name: 🛡️ Check out trusted Dependabot guardrails + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event.pull_request.base.sha }} + path: .dependabot-guard + token: ${{ steps.generate_token.outputs.token }} + persist-credentials: false + - name: 🤖 Fetch Dependabot metadata id: metadata uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0 @@ -88,13 +96,37 @@ jobs: env: GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} PR_NUMBER: ${{ github.event.pull_request.number }} + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} run: | set -euo pipefail FILES=$(gh api "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/files" --paginate --jq '.[].filename') MATCHING_FILES=$(printf '%s\n' "$FILES" | sed '/^$/d' | grep -E '^src/DLLPickle\.Build/(DLLPickle\.csproj|packages\.lock\.json)$' || true) UNEXPECTED_FILES=$(printf '%s\n' "$FILES" | sed '/^$/d' | grep -Ev '^src/DLLPickle\.Build/(DLLPickle\.csproj|packages\.lock\.json)$' || true) + CSPROJ_CHANGED=$(printf '%s\n' "$FILES" | grep -Fx 'src/DLLPickle.Build/DLLPickle.csproj' || true) + CSPROJ_PATCH=$(gh api "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/files" --paginate --jq '.[] | select(.filename == "src/DLLPickle.Build/DLLPickle.csproj") | if has("patch") then .patch else "__DLLPICKLE_PATCH_UNAVAILABLE__" end') + PATCH_UNAVAILABLE=$(printf '%s\n' "$CSPROJ_PATCH" | grep -F '__DLLPICKLE_PATCH_UNAVAILABLE__' || true) + CONDITIONAL_TFM_ADDITION=$(printf '%s\n' "$CSPROJ_PATCH" | grep -E '^\+.*\$\(TargetFramework\)' || true) + PROJECT_PATCH_VALID=true + + if [ -n "$CSPROJ_CHANGED" ]; then + BASE_PROJECT="$RUNNER_TEMP/DLLPickle.base.csproj" + CANDIDATE_PROJECT="$RUNNER_TEMP/DLLPickle.candidate.csproj" + gh api --method GET -H 'Accept: application/vnd.github.raw+json' \ + "repos/$GITHUB_REPOSITORY/contents/src/DLLPickle.Build/DLLPickle.csproj" \ + -f "ref=$BASE_SHA" > "$BASE_PROJECT" + gh api --method GET -H 'Accept: application/vnd.github.raw+json' \ + "repos/$GITHUB_REPOSITORY/contents/src/DLLPickle.Build/DLLPickle.csproj" \ + -f "ref=$HEAD_SHA" > "$CANDIDATE_PROJECT" + if ! pwsh -NoProfile -NonInteractive -File \ + ./.dependabot-guard/tools/Test-DLLPicklePackageReferenceUpdate.ps1 \ + -BaseProjectPath "$BASE_PROJECT" \ + -CandidateProjectPath "$CANDIDATE_PROJECT"; then + PROJECT_PATCH_VALID=false + fi + fi - if [ -n "$MATCHING_FILES" ] && [ -z "$UNEXPECTED_FILES" ]; then + if [ -n "$MATCHING_FILES" ] && [ -z "$UNEXPECTED_FILES" ] && [ -z "$PATCH_UNAVAILABLE" ] && [ -z "$CONDITIONAL_TFM_ADDITION" ] && [ "$PROJECT_PATCH_VALID" = true ]; then echo "is_exact_nuget_update=true" >> "$GITHUB_OUTPUT" else echo "is_exact_nuget_update=false" >> "$GITHUB_OUTPUT" @@ -102,6 +134,16 @@ jobs: echo "Refusing auto-approval because the PR changes files outside the NuGet allow-list:" printf '%s\n' "$UNEXPECTED_FILES" fi + if [ -n "$PATCH_UNAVAILABLE" ]; then + echo "Refusing auto-approval because the Files API omitted the DLLPickle.csproj patch." + fi + if [ -n "$CONDITIONAL_TFM_ADDITION" ]; then + echo "Refusing auto-approval because the PR introduces per-TFM project content:" + printf '%s\n' "$CONDITIONAL_TFM_ADDITION" + fi + if [ "$PROJECT_PATCH_VALID" != true ]; then + echo "Refusing auto-approval because DLLPickle.csproj contains changes beyond version-only updates to existing PackageReference entries." + fi fi # The repository ruleset requires Build gate, Validate upstream compatibility tooling, and @@ -163,19 +205,24 @@ jobs: **Package:** ${NUGET_LINK} — see the package's Release Notes / changelog for breaking changes. - **TFM alignment (§8.2 Step 0)** + **Per-TFM major-update evidence (required before marking ready)** - - 0a — Build gate: restore/build/test green on \`net8.0\` under \`--locked-mode\`. See the Checks tab: ${PR_URL}/checks - - 0b — explicit net8.0-consumable asset inspection (including \`netstandard2.0\` and \`netstandard2.1\` where applicable): run \`tools/Test-DLLPickleTfmAlignment.ps1\` against the bumped package(s); the scheduled Upstream-Compatibility candidate flow runs this fail-closed. + | TFM | Resolved graph + selected assets | Added/removed/changed assemblies | Conflict-surface delta | Scenario outcomes | + |---|---|---|---|---| + | \`net8.0\` | \`tfm-alignment.json\` | package composition/size artifacts | PowerShell 7.4 Windows/Linux/macOS profile artifacts | exact PowerShell 7.4 result artifacts | + | \`net9.0\` | \`tfm-alignment.json\` | package composition/size artifacts | PowerShell 7.5 Windows/Linux/macOS profile artifacts | exact PowerShell 7.5 result artifacts | + | \`net10.0\` | \`tfm-alignment.json\` | package composition/size artifacts | PowerShell 7.6 Windows/Linux/macOS profile artifacts | exact PowerShell 7.6 result artifacts | - **Pester / CI:** ${PR_URL}/checks + The structured JSON reports are attached to the required **Build gate** and **Validate upstream compatibility tooling** checks: ${PR_URL}/checks. Missing, stale, unaccepted, or drifted profile evidence fails closed; this comment is an index, not a substitute for the artifacts. - **Conflict surface / policy impact:** a major upstream jump can move the conflict surface. Re-check the Upstream-Compatibility drift gate and re-adjudicate \`build/dependency-policy.json\` (classification + evidence + baseline) per §3 if it moved. + **Size policy:** \`artifact-size.json\` must remain within \`build/artifact-size-baseline.json\`; a material breach requires explicit review even when the nominal update is minor. **Maintainer checklist (complete before marking ready)** - [ ] Re-adjudicate the §3 preload/block classifications for the bumped package(s). - - [ ] Confirm TFM alignment: 0a (Build gate green) and 0b (\`Test-DLLPickleTfmAlignment.ps1\`). - - [ ] Refresh the \`build/dependency-policy.json\` baseline if the conflict surface moved. - - [ ] Auth-tier real-environment sign-off per §9 (the 2.0.1 precedent) for any bundled-set change. + - [ ] Confirm all three resolved graphs and selected-asset rows in \`tfm-alignment.json\`. + - [ ] Review added, removed, and hash-changed assemblies plus per-TFM/full size deltas. + - [ ] Re-adjudicate each profile/platform conflict fingerprint and both import orders. + - [ ] Review every exact-runtime scenario outcome and preserve #174 process isolation. + - [ ] Complete the authenticated read-only release gates listed in \`docs/generated/Compatibility-Evidence.md\`; do not represent unexecuted probes as passing. - [ ] Merge carrying a \`breaking:\` prefix so the release publishes a **major** module version. ⚠️ The default squash subject is Dependabot's \`deps:\` commit message, which \`Get-VersionBump.ps1\` maps to a **minor** bump — edit the squash commit subject to \`breaking:\` before confirming the merge, or the release will publish the wrong (minor) version." diff --git a/.github/workflows/PowerShell-Support-Lifecycle.yml b/.github/workflows/PowerShell-Support-Lifecycle.yml new file mode 100644 index 00000000..f024aedf --- /dev/null +++ b/.github/workflows/PowerShell-Support-Lifecycle.yml @@ -0,0 +1,375 @@ +name: PowerShell Support Lifecycle + +on: + schedule: + - cron: "17 9 * * 1" + workflow_dispatch: + +# Discovery and archive validation remain read-only. Dedicated publication jobs use +# stable fingerprints to suppress duplicate pull requests, issues, and comments. +permissions: + contents: read + +concurrency: + group: powershell-support-lifecycle + cancel-in-progress: false + +jobs: + discover: + name: Discover supported PowerShell updates + runs-on: ubuntu-24.04 + timeout-minutes: 10 + outputs: + has_patch: ${{ steps.proposal.outputs.has_patch }} + runtime_matrix: ${{ steps.proposal.outputs.runtime_matrix }} + support_review: ${{ steps.proposal.outputs.support_review }} + policy_current: ${{ steps.lifecycle-policy.outcome == 'success' }} + steps: + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Discover servicing and support-contract updates + shell: pwsh + run: ./tools/Get-DLLPicklePowerShellSupportUpdate.ps1 -OutputPath ./artifacts/lifecycle/powershell-support-update.json + + - name: Prepare a checksum-pinned candidate matrix + id: proposal + shell: pwsh + run: | + $ReportPath = './artifacts/lifecycle/powershell-support-update.json' + $CandidatePath = './artifacts/lifecycle/candidate-test-matrix.json' + $Report = Get-Content -LiteralPath $ReportPath -Raw | ConvertFrom-Json + $HasPatch = [bool]$Report.MatrixOnlyUpdateAvailable + if ($HasPatch) { + $Parameters = @{ + UpdateReportPath = $ReportPath + OutputPath = $CandidatePath + PrepareCandidate = $true + } + ./tools/Update-DLLPicklePowerShellTestMatrix.ps1 @Parameters | Out-Null + } else { + Copy-Item -LiteralPath ./build/powershell-test-matrix.json -Destination $CandidatePath + } + $RuntimeMatrix = ./tools/New-DLLPicklePowerShellTestMatrix.ps1 -MatrixPath $CandidatePath -Compress + "has_patch=$($HasPatch.ToString().ToLowerInvariant())" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8 + "runtime_matrix=$RuntimeMatrix" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8 + "support_review=$(([bool]$Report.SupportContractReviewRequired).ToString().ToLowerInvariant())" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8 + + - name: Summarize support update discovery + if: always() && hashFiles('artifacts/lifecycle/powershell-support-update.json') != '' + shell: pwsh + run: | + $Report = Get-Content -LiteralPath './artifacts/lifecycle/powershell-support-update.json' -Raw | ConvertFrom-Json + @( + '### PowerShell support update discovery' + '' + "- Patch updates: $(@($Report.PatchUpdates).Count)" + "- New GA lines: $(@($Report.NewLines).Count)" + "- Support-contract review required: $($Report.SupportContractReviewRequired)" + "- Patch publication fingerprint: ``$($Report.PatchProposalFingerprint)``" + "- Support-contract fingerprint: ``$($Report.SupportContractFingerprint)``" + '- Publishing status: validated findings are published once per stable fingerprint' + ) | Add-Content -LiteralPath $env:GITHUB_STEP_SUMMARY + + - name: Check lifecycle evidence and retirement windows + id: lifecycle-policy + continue-on-error: true + shell: pwsh + run: ./tools/Test-DLLPickleRuntimeProfilePolicy.ps1 -Mode Scheduled + + - name: Upload support discovery evidence + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: powershell-support-discovery + path: ./artifacts/lifecycle + if-no-files-found: error + retention-days: 30 + + candidate-runtime: + name: Validate proposed PowerShell ${{ matrix.powerShellVersion }} on ${{ matrix.platform }} + needs: discover + if: ${{ needs.discover.outputs.has_patch == 'true' }} + runs-on: ${{ matrix.runner }} + timeout-minutes: 20 + permissions: + contents: read + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.discover.outputs.runtime_matrix) }} + steps: + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Download checksum-pinned candidate matrix + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: powershell-support-discovery + path: ./downloaded-support-discovery + + - name: Provision and verify the proposed official archive + shell: pwsh + env: + MATRIX_POWERSHELL_VERSION: ${{ matrix.powerShellVersion }} + MATRIX_PLATFORM: ${{ matrix.platform }} + MATRIX_ARCHITECTURE: ${{ matrix.architecture }} + RUNTIME_INSTALL_ROOT: ${{ runner.temp }}/dllpickle-support-candidate + RUNTIME_IDENTITY_PATH: ./runtime-identity-${{ matrix.powerShellVersion }}-${{ matrix.platform }}.json + run: | + $Parameters = @{ + Provider = 'DirectArchive' + PowerShellVersion = $env:MATRIX_POWERSHELL_VERSION + Platform = $env:MATRIX_PLATFORM + Architecture = $env:MATRIX_ARCHITECTURE + InstallRoot = $env:RUNTIME_INSTALL_ROOT + MatrixPath = './downloaded-support-discovery/candidate-test-matrix.json' + PassThru = $true + } + $Identity = ./tools/Install-DLLPickleTestPowerShell.ps1 @Parameters + $Identity | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $env:RUNTIME_IDENTITY_PATH -Encoding utf8 + + - name: Upload proposed runtime identity + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: powershell-update-runtime-${{ matrix.powerShellVersion }}-${{ matrix.platform }}-${{ matrix.architecture }} + path: ./runtime-identity-*.json + if-no-files-found: error + retention-days: 30 + + finalize-patch-proposal: + name: Finalize verified PowerShell patch proposal + needs: [discover, candidate-runtime] + if: ${{ always() && needs.discover.outputs.has_patch == 'true' && needs.discover.outputs.policy_current == 'true' && needs.candidate-runtime.result == 'success' }} + runs-on: ubuntu-24.04 + timeout-minutes: 10 + permissions: + contents: write + pull-requests: write + steps: + - name: Create GitHub App token for servicing publication + id: app-token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + token: ${{ steps.app-token.outputs.token }} + + - name: Download discovery evidence + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: powershell-support-discovery + path: ./downloaded-support-discovery + + - name: Download all proposed runtime identities + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + pattern: powershell-update-runtime-* + path: ./downloaded-runtime-identities + merge-multiple: true + + - name: Finalize and validate the reviewable matrix proposal + shell: pwsh + run: | + $ProposalRoot = './artifacts/lifecycle/proposal' + $MatrixPath = Join-Path $ProposalRoot 'powershell-test-matrix.json' + $Parameters = @{ + UpdateReportPath = './downloaded-support-discovery/powershell-support-update.json' + RuntimeIdentityPath = './downloaded-runtime-identities' + OutputPath = $MatrixPath + } + ./tools/Update-DLLPicklePowerShellTestMatrix.ps1 @Parameters | Out-Null + ./tools/Test-DLLPickleRuntimeProfilePolicy.ps1 -TestMatrixPath $MatrixPath -Mode Scheduled + ./tools/New-DLLPickleSupportDocumentation.ps1 -TestMatrixPath $MatrixPath -OutputDirectory (Join-Path $ProposalRoot 'generated-docs') | Out-Null + @( + '### Verified PowerShell servicing proposal' + '' + '- Official archives: checksum and runtime identity validated on all nine cells' + '- Output: reviewable matrix and generated documentation artifacts' + '- Publication: one pull request per stable validated fingerprint' + ) | Add-Content -LiteralPath $env:GITHUB_STEP_SUMMARY + + - name: Upload verified patch proposal + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: powershell-support-patch-proposal + path: ./artifacts/lifecycle/proposal + if-no-files-found: error + retention-days: 30 + + - name: Publish one servicing-patch pull request per validated fingerprint + shell: pwsh + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + $ErrorActionPreference = 'Stop' + $ProposalRoot = './artifacts/lifecycle/proposal' + $Report = Get-Content -LiteralPath './downloaded-support-discovery/powershell-support-update.json' -Raw | ConvertFrom-Json + $Fingerprint = [string]$Report.PatchProposalFingerprint + if ($Fingerprint -notmatch '^[a-f0-9]{64}$') { + throw 'The validated patch proposal did not contain a stable SHA-256 publication fingerprint.' + } + + $BranchName = "automation/powershell-patch-$($Fingerprint.Substring(0, 16))" + $ExistingPullRequests = @(gh pr list --state all --head $BranchName --limit 10 --json number,state,url,body | ConvertFrom-Json) + if ($LASTEXITCODE -ne 0) { + throw 'Failed to query existing PowerShell servicing pull requests.' + } + if ($ExistingPullRequests.Count -gt 0) { + "Fingerprint already published as $($ExistingPullRequests[0].url); no duplicate pull request created." | Add-Content -LiteralPath $env:GITHUB_STEP_SUMMARY + exit 0 + } + + Copy-Item -LiteralPath (Join-Path $ProposalRoot 'powershell-test-matrix.json') -Destination './build/powershell-test-matrix.json' + foreach ($GeneratedDocument in @(Get-ChildItem -LiteralPath (Join-Path $ProposalRoot 'generated-docs') -File)) { + Copy-Item -LiteralPath $GeneratedDocument.FullName -Destination (Join-Path './docs/generated' $GeneratedDocument.Name) + } + + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git switch -c $BranchName + git add build/powershell-test-matrix.json docs/generated + git diff --cached --quiet + if ($LASTEXITCODE -eq 0) { + Write-Host 'The validated proposal is already present in the checked-out branch; no pull request is needed.' + exit 0 + } + git commit -m 'ci: update PowerShell servicing patches' + if ($LASTEXITCODE -ne 0) { + throw 'Failed to commit the validated PowerShell servicing proposal.' + } + git push --set-upstream origin $BranchName + if ($LASTEXITCODE -ne 0) { + throw 'Failed to publish the validated PowerShell servicing branch.' + } + + $Versions = @($Report.PatchUpdates.CandidateVersion | Sort-Object) -join ', ' + $Title = "ci: update PowerShell servicing patches to $Versions" + $Body = @( + 'Automated PowerShell servicing-patch proposal.' + '' + "Candidate patches: $Versions" + '- Official archive checksums verified for Windows, Linux, and macOS x64.' + '- Exact runtime identity verified in all proposed matrix cells.' + '- Generated support documentation refreshed from the validated matrix.' + '- Support-line additions and retirements are intentionally excluded.' + '' + "Validation run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + '' + [string]$Report.PatchProposalMarker + ) -join [System.Environment]::NewLine + $Arguments = @('pr', 'create', '--title', $Title, '--body', $Body, '--base', 'main', '--head', $BranchName) + & gh @Arguments + if ($LASTEXITCODE -ne 0) { + throw 'Failed to open the validated PowerShell servicing pull request.' + } + + support-contract-warning: + name: Require review for new or retiring support lines + needs: discover + if: ${{ needs.discover.outputs.support_review == 'true' }} + runs-on: ubuntu-24.04 + permissions: + contents: read + issues: write + steps: + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Download discovery evidence + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: powershell-support-discovery + path: ./downloaded-support-discovery + + - name: Publish one support-contract finding per stable fingerprint + shell: pwsh + env: + GH_TOKEN: ${{ github.token }} + run: | + $ErrorActionPreference = 'Stop' + $Report = Get-Content -LiteralPath './downloaded-support-discovery/powershell-support-update.json' -Raw | ConvertFrom-Json + $Fingerprint = [string]$Report.SupportContractFingerprint + if ($Fingerprint -notmatch '^[a-f0-9]{64}$') { + throw 'The support-contract report did not contain a stable SHA-256 publication fingerprint.' + } + + $Title = 'PowerShell support-contract review required' + $Body = @( + 'Automated lifecycle discovery found a support-contract decision that must not be applied without maintainer review.' + '' + 'New GA lines:' + $(if (@($Report.NewLines).Count -eq 0) { '- None' } else { @($Report.NewLines | Sort-Object ReleaseLine | ForEach-Object { "- $($_.ReleaseLine): latest $($_.LatestVersion), lifecycle end $($_.LifecycleEndDate); CLR/TFM mapping and initial evidence pending" }) }) + '' + 'Retiring or expired declared lines:' + $(if (@($Report.Lifecycle | Where-Object Status -IN @('RetiringSoon', 'Expired')).Count -eq 0) { '- None' } else { @($Report.Lifecycle | Where-Object Status -IN @('RetiringSoon', 'Expired') | Sort-Object ReleaseLine | ForEach-Object { "- $($_.ReleaseLine): $($_.Status), lifecycle end $($_.LifecycleEnd), $($_.DaysRemaining) day(s) remaining" }) }) + '' + 'Lifecycle date changes:' + $(if (@($Report.LifecycleDateChanges).Count -eq 0) { '- None' } else { @($Report.LifecycleDateChanges | Sort-Object ReleaseLine | ForEach-Object { "- $($_.ReleaseLine): matrix $($_.MatrixLifecycleEnd), live $($_.LifecycleEnd)" }) }) + '' + "Declared lines missing from live lifecycle data: $(@($Report.LifecycleMissingLines) -join ', ')" + "Supported lines not declared by DLLPickle: $(@($Report.UndeclaredSupportedLines) -join ', ')" + '' + 'Required decision: review the support contract. Adding or removing a line changes the loader, shipped TFMs, package size, documentation, and profile-aware compatibility evidence.' + '' + "Discovery run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + '' + [string]$Report.SupportContractMarker + ) -join [System.Environment]::NewLine + + $Candidates = @(gh issue list --state all --search "$Title in:title" --limit 20 --json number,state,title | ConvertFrom-Json | Where-Object title -EQ $Title) + if ($LASTEXITCODE -ne 0) { + throw 'Failed to query existing PowerShell support-contract issues.' + } + $ExistingIssue = $Candidates | Select-Object -First 1 + $AlreadyReported = $false + if ($ExistingIssue) { + $ExistingContent = gh issue view $ExistingIssue.number --json body,comments | ConvertFrom-Json + if ($LASTEXITCODE -ne 0) { + throw 'Failed to read the existing PowerShell support-contract issue.' + } + $PublishedText = @($ExistingContent.body) + @($ExistingContent.comments.body) + $AlreadyReported = ./tools/Test-DLLPickleFindingFingerprintReported.ps1 -Fingerprint $Fingerprint -Text $PublishedText + } + + if ($AlreadyReported) { + Write-Host "Support-contract fingerprint $Fingerprint is already reported; suppressing a duplicate comment." + } elseif ($ExistingIssue) { + if ([string]$ExistingIssue.state -eq 'CLOSED') { + gh issue reopen $ExistingIssue.number + if ($LASTEXITCODE -ne 0) { throw 'Failed to reopen the support-contract issue for a new finding.' } + } + gh issue comment $ExistingIssue.number --body $Body + if ($LASTEXITCODE -ne 0) { throw 'Failed to publish the new support-contract finding.' } + } else { + gh issue create --title $Title --body $Body + if ($LASTEXITCODE -ne 0) { throw 'Failed to open the support-contract review issue.' } + } + + throw 'A new, changing, retiring, or expired PowerShell support line requires maintainer review; automatic contract mutation is blocked.' + + lifecycle-policy-gate: + name: Enforce current lifecycle policy after publication + needs: [discover, support-contract-warning] + if: ${{ always() }} + runs-on: ubuntu-24.04 + steps: + - name: Enforce fail-closed lifecycle result + shell: pwsh + env: + LIFECYCLE_POLICY_CURRENT: ${{ needs.discover.outputs.policy_current }} + run: | + if ($env:LIFECYCLE_POLICY_CURRENT -ne 'true') { + throw 'The declared runtime lifecycle policy is expired, stale, or otherwise not current. Discovery evidence and any support-contract finding were published before this gate failed.' + } + Write-Host 'The declared runtime lifecycle policy is current.' diff --git a/.github/workflows/Release-and-Publish.yml b/.github/workflows/Release-and-Publish.yml index cada43ca..f431426b 100644 --- a/.github/workflows/Release-and-Publish.yml +++ b/.github/workflows/Release-and-Publish.yml @@ -46,12 +46,173 @@ env: MODULE_NAME: DLLPickle MODULE_DIR: ./module/DLLPickle MANIFEST_PATH: ./src/DLLPickle/DLLPickle.psd1 + AUTHENTICATED_WORKFLOW_FILE: Authenticated-Compatibility.yml + AUTHENTICATED_EVIDENCE_ARTIFACT: authenticated-compatibility-evidence + MANUAL_AUTHENTICATED_EVIDENCE_PATH: ./build/authenticated-evidence/initial-multitarget-major.json jobs: + authenticated-release-gate: + name: Require Authenticated Compatibility + runs-on: ubuntu-latest + timeout-minutes: 10 + # Prefer exact-commit evidence from the future protected credentialed workflow. Until that + # environment exists, one reviewed manual transition record may authorize only the initial + # 3.0.0 bundle fingerprint and expires within 30 days. Neither route permits writes. + if: | + github.repository_owner == 'SamErde' && + ( + github.event_name == 'workflow_dispatch' || + (github.event_name == 'pull_request' && github.event.pull_request.merged == true) + ) + + permissions: + actions: read + contents: read + + outputs: + release_sha: ${{ steps.release-candidate.outputs.release_sha }} + evidence_mode: ${{ steps.authenticated-evidence.outputs.evidence_mode }} + allowed_release_version: ${{ steps.authenticated-evidence.outputs.allowed_release_version }} + + steps: + - name: Checkout release candidate + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + ref: main + persist-credentials: false + + - name: Resolve immutable release candidate + id: release-candidate + shell: pwsh + run: | + $ErrorActionPreference = 'Stop' + $ReleaseSha = (git rev-parse HEAD).Trim() + if ($ReleaseSha -notmatch '^[a-f0-9]{40}$') { + throw "Could not resolve the checked-out main commit as an immutable release candidate: '$ReleaseSha'." + } + "release_sha=$ReleaseSha" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8 + Write-Host "Immutable release candidate: $ReleaseSha" + + - name: Validate authenticated release policy + shell: pwsh + run: | + $Policy = Get-Content -LiteralPath './build/dependency-policy.json' -Raw | ConvertFrom-Json + $RequiredProfiles = @( + $Policy.runtimeProfiles | Where-Object { + $_.validationTiers.authenticatedReadOnly.requiredBeforeRelease -eq $true + } + ) + if ($RequiredProfiles.Count -eq 0) { + throw 'No runtime profile declares the authenticated read-only tier as required before release.' + } + + $WriteEnabledProfiles = @( + $RequiredProfiles | Where-Object { + $_.validationTiers.authenticatedReadOnly.writesAllowed -ne $false + } + ) + if ($WriteEnabledProfiles.Count -gt 0) { + throw 'A required authenticated release profile does not explicitly prohibit writes.' + } + + - name: Require protected or bounded authenticated evidence + id: authenticated-evidence + shell: pwsh + env: + GH_TOKEN: ${{ github.token }} + RELEASE_SHA: ${{ steps.release-candidate.outputs.release_sha }} + run: | + $ErrorActionPreference = 'Stop' + $EvidenceSha = $env:RELEASE_SHA + if ($EvidenceSha -notmatch '^[a-f0-9]{40}$') { + throw "Could not resolve the exact release-candidate SHA for authenticated evidence: '$EvidenceSha'." + } + + $RunArguments = @( + 'run', 'list' + '--repo', $env:GITHUB_REPOSITORY + '--workflow', $env:AUTHENTICATED_WORKFLOW_FILE + '--commit', $EvidenceSha + '--status', 'success' + '--limit', '20' + '--json', 'databaseId,headSha,conclusion,createdAt,url' + ) + $ProtectedEvidenceRun = $null + $ProtectedWorkflowPath = Join-Path '.github/workflows' $env:AUTHENTICATED_WORKFLOW_FILE + $ProtectedWorkflowConfigured = Test-Path -LiteralPath $ProtectedWorkflowPath -PathType Leaf + if ($ProtectedWorkflowConfigured) { + $RunJson = & gh @RunArguments 2>$null + if ($LASTEXITCODE -ne 0) { + throw "Could not query protected authenticated workflow '$($env:AUTHENTICATED_WORKFLOW_FILE)' for exact commit '$EvidenceSha'." + } + $MatchingRuns = @( + $RunJson | ConvertFrom-Json | Where-Object { + $_.headSha -eq $EvidenceSha -and $_.conclusion -eq 'success' + } + ) + foreach ($CandidateRun in @($MatchingRuns | Sort-Object createdAt -Descending)) { + $ArtifactJson = & gh api "/repos/$($env:GITHUB_REPOSITORY)/actions/runs/$($CandidateRun.databaseId)/artifacts" 2>$null + if ($LASTEXITCODE -ne 0) { + throw "Could not query artifacts for protected authenticated workflow run '$($CandidateRun.databaseId)'." + } + $EvidenceArtifacts = @( + ($ArtifactJson | ConvertFrom-Json).artifacts | Where-Object { + $_.name -eq $env:AUTHENTICATED_EVIDENCE_ARTIFACT -and -not $_.expired + } + ) + if ($EvidenceArtifacts.Count -eq 1) { + $ProtectedEvidenceRun = $CandidateRun + break + } + } + if ($MatchingRuns.Count -gt 0 -and -not $ProtectedEvidenceRun) { + throw "Successful protected authenticated workflow runs exist for '$EvidenceSha', but none has exactly one unexpired '$($env:AUTHENTICATED_EVIDENCE_ARTIFACT)' artifact." + } + } else { + Write-Information -MessageData "Protected authenticated workflow '$($env:AUTHENTICATED_WORKFLOW_FILE)' is not configured; evaluating the bounded manual transition only." -InformationAction Continue + } + + if ($ProtectedEvidenceRun) { + $EvidenceMode = 'protected-exact-commit-workflow' + $AllowedReleaseVersion = '' + $EvidenceReference = $ProtectedEvidenceRun.url + } elseif (-not $ProtectedWorkflowConfigured -and (Test-Path -LiteralPath $env:MANUAL_AUTHENTICATED_EVIDENCE_PATH -PathType Leaf)) { + $ManualEvidenceParameters = @{ + EvidencePath = $env:MANUAL_AUTHENTICATED_EVIDENCE_PATH + RepositoryRoot = '.' + TestMatrixPath = './build/powershell-test-matrix.json' + DependencyPolicyPath = './build/dependency-policy.json' + Mode = 'Release' + } + $ManualEvidence = ./tools/Test-DLLPickleManualAuthenticatedEvidence.ps1 @ManualEvidenceParameters + $EvidenceMode = 'manual-interactive-transition' + $AllowedReleaseVersion = [string]$ManualEvidence.AllowedReleaseVersion + $EvidenceReference = "``$($env:MANUAL_AUTHENTICATED_EVIDENCE_PATH)`` (expires $($ManualEvidence.ExpiresAtUtc))" + } elseif ($ProtectedWorkflowConfigured) { + throw "Release is blocked: protected authenticated workflow '$($env:AUTHENTICATED_WORKFLOW_FILE)' is configured but has no successful exact-commit evidence with one unexpired '$($env:AUTHENTICATED_EVIDENCE_ARTIFACT)' artifact." + } else { + throw 'Release is blocked: the protected authenticated workflow is not configured and no accepted bounded manual transition evidence is committed.' + } + + "evidence_mode=$EvidenceMode" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8 + "allowed_release_version=$AllowedReleaseVersion" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8 + + @( + '### Authenticated release evidence' + '' + "- Candidate SHA: ``$EvidenceSha``" + "- Evidence mode: ``$EvidenceMode``" + "- Evidence reference: $EvidenceReference" + $(if ($AllowedReleaseVersion) { "- Allowed release version: ``$AllowedReleaseVersion``" }) + '- Required profiles prohibit writes: True' + ) | Add-Content -LiteralPath $env:GITHUB_STEP_SUMMARY + analyze: name: Analyze and Determine Version runs-on: ubuntu-latest timeout-minutes: 10 + needs: authenticated-release-gate # Only run for merged PRs to main or manual dispatch if: | github.repository_owner == 'SamErde' && @@ -114,10 +275,26 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 - # Releases always target main (create-tag, build, and publish all use main), so analyze the - # commit history on main too. This keeps the bump calculation consistent with the released - # commit even for manual workflow_dispatch runs started from a non-main branch. - ref: main + # Use the same immutable main commit accepted by the authenticated evidence gate. + ref: ${{ needs.authenticated-release-gate.outputs.release_sha }} + persist-credentials: false + + - name: Refresh official PowerShell servicing state + shell: pwsh + run: ./tools/Get-DLLPicklePowerShellSupportUpdate.ps1 -OutputPath ./artifacts/lifecycle/powershell-support-update.json -RequireCurrent + + - name: Validate supported PowerShell lifecycle policy + shell: pwsh + run: ./tools/Test-DLLPickleRuntimeProfilePolicy.ps1 -Mode Release -LifecycleEvidencePath ./artifacts/lifecycle/powershell-support-update.json + + - name: Upload release lifecycle evidence + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: release-lifecycle-evidence + path: ./artifacts/lifecycle/powershell-support-update.json + if-no-files-found: warn + retention-days: 30 - name: Determine version and bump type id: determine_version @@ -126,6 +303,9 @@ jobs: EVENT_NAME: ${{ github.event_name }} MANUAL_BUMP_INPUT: ${{ inputs.version_bump }} MANUAL_BUMP_EVENT: ${{ github.event.inputs.version_bump }} + RELEASE_SHA: ${{ needs.authenticated-release-gate.outputs.release_sha }} + AUTHENTICATED_EVIDENCE_MODE: ${{ needs.authenticated-release-gate.outputs.evidence_mode }} + AUTHENTICATED_ALLOWED_RELEASE_VERSION: ${{ needs.authenticated-release-gate.outputs.allowed_release_version }} run: | # Pass manual bump parameter to Get-VersionBump for centralized version logic Write-Host "Analyzing version requirements..." @@ -174,12 +354,25 @@ jobs: $NewVersion = $VersionResult.NewVersion $CurrentVersion = [version]$VersionResult.CurrentVersion + if ($ShouldRelease -and $env:AUTHENTICATED_EVIDENCE_MODE -eq 'manual-interactive-transition') { + if ([string]::IsNullOrWhiteSpace($env:AUTHENTICATED_ALLOWED_RELEASE_VERSION) -or + [string]$NewVersion -ne $env:AUTHENTICATED_ALLOWED_RELEASE_VERSION) { + throw "Bounded manual authenticated evidence permits only version '$($env:AUTHENTICATED_ALLOWED_RELEASE_VERSION)', not '$NewVersion'." + } + } + Write-Host "`nVersion Analysis Results:" Write-Host "Current version: $($CurrentVersion.ToString())" Write-Host "Version bump type: $NewVersionType" Write-Host "New version: $NewVersion" Write-Host "Should release: $ShouldRelease" + $CheckedOutSha = (git rev-parse HEAD).Trim() + if ($CheckedOutSha -ne $env:RELEASE_SHA) { + throw "Analyze checked out $CheckedOutSha, but authenticated evidence approved $($env:RELEASE_SHA)." + } + $ReleaseSha = $env:RELEASE_SHA + # Determine workflow trigger if ($env:EVENT_NAME -eq "workflow_dispatch") { $TriggerSource = "manual" @@ -207,12 +400,12 @@ jobs: "current_version=$($CurrentVersion.ToString())" >> $env:GITHUB_OUTPUT "previous_tag=$PreviousTag" >> $env:GITHUB_OUTPUT "trigger_source=$TriggerSource" >> $env:GITHUB_OUTPUT + "release_sha=$ReleaseSha" >> $env:GITHUB_OUTPUT exit 0 } - # Set outputs. Capture the resolved main commit so every downstream job (tag, build, - # sync, release, publish) operates on exactly this SHA, even if main advances mid-release. - $ReleaseSha = (git rev-parse HEAD).Trim() + # Set outputs. Every downstream job (tag, build, sync, release, publish) operates on the + # exact commit accepted by the authenticated release gate, even if main advances. "should_release=true" >> $env:GITHUB_OUTPUT "version_bump=$NewVersionType" >> $env:GITHUB_OUTPUT "current_version=$($CurrentVersion.ToString())" >> $env:GITHUB_OUTPUT @@ -382,6 +575,33 @@ jobs: exit 1 } + - name: Revalidate stamped release artifact + shell: pwsh + run: | + $ErrorActionPreference = 'Stop' + $CompositionParameters = @{ + ModulePath = './module/DLLPickle' + SkipBuildOutputComparison = $true + OutputPath = './artifacts/release/artifact-composition.json' + Strict = $true + } + ./tools/Test-DLLPicklePackageArtifact.ps1 @CompositionParameters + + $SizeParameters = @{ + ModulePath = './module/DLLPickle' + OutputPath = './artifacts/release/artifact-size.json' + Strict = $true + } + ./tools/New-DLLPickleArtifactSizeReport.ps1 @SizeParameters + + - name: Upload stamped release policy reports + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: stamped-release-policy-reports + path: ./artifacts/release + if-no-files-found: warn + - name: Upload updated module uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: @@ -748,7 +968,7 @@ jobs: name: Release Summary runs-on: ubuntu-latest timeout-minutes: 5 - needs: [analyze, update-version, build-and-test, create-release, publish, rollback-on-failure, sync-module-manifest] + needs: [authenticated-release-gate, analyze, update-version, build-and-test, create-release, publish, rollback-on-failure, sync-module-manifest] if: always() permissions: @@ -770,6 +990,7 @@ jobs: ### Workflow Status | Step | Status | |------|--------| + | Authenticated Compatibility | ${{ needs.authenticated-release-gate.result }} | | Analyze | ${{ needs.analyze.result }} | | Create Tag | ${{ needs.update-version.result }} | | Build & Test | ${{ needs.build-and-test.result }} | diff --git a/.github/workflows/Upstream-Compatibility.yml b/.github/workflows/Upstream-Compatibility.yml index 00dd91b5..9e29f3e0 100644 --- a/.github/workflows/Upstream-Compatibility.yml +++ b/.github/workflows/Upstream-Compatibility.yml @@ -56,17 +56,32 @@ jobs: $Files = gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" --paginate --jq '.[] | .filename, (.previous_filename // empty)' # Native non-zero exits don't reliably throw in pwsh, so check explicitly -> fail-safe. if ($LASTEXITCODE -ne 0) { throw "gh api exited $LASTEXITCODE while listing PR files" } - $Patterns = @('^build/', '^src/DLLPickle\.Build/', '^tests/', '^tools/', '^\.github/workflows/Upstream-Compatibility\.yml$') + $Patterns = @('^build/', '^src/DLLPickle\.Build/', '^src/DLLPickle/', '^tests/', '^tools/', '^global\.json$', '^\.github/workflows/Upstream-Compatibility\.yml$') # Any change that can alter the accepted baseline or the fingerprint computation must # prove itself against a fresh upstream inventory. Comparator/tests/workflow-only changes # stay on the deterministic smoke path so a pre-existing external drift does not prevent # the guardrail itself from being repaired. $LivePatterns = @( '^build/dependency-policy\.json$', + '^build/DLLPickle\.Build\.ps1$', + '^build/DLLPickle\.Settings\.ps1$', + '^build/DLLPickle\.Tooling\.ps1$', + '^build/build-tool-versions\.json$', + '^global\.json$', '^src/DLLPickle\.Build/DLLPickle\.csproj$', '^src/DLLPickle\.Build/packages\.lock\.json$', + '^build/powershell-test-matrix\.json$', '^tools/Get-DLLPickleUpstreamInventory\.ps1$', - '^tools/New-DLLPickleConflictMatrix\.ps1$' + '^tools/Get-DLLPickleLoadedTrackedAssembly\.ps1$', + '^tools/Get-DLLPickleRuntimeAssemblySnapshot\.ps1$', + '^tools/DLLPickle\.ProfileEvidence\.ps1$', + '^tools/Invoke-DLLPickleBuild\.ps1$', + '^tools/New-DLLPickleConflictMatrix\.ps1$', + '^tools/New-DLLPickleNormalizedProfileEvidence\.ps1$', + '^tools/New-DLLPickleUpstreamScenarioEvidence\.ps1$', + '^tools/Test-DLLPickleProfileConflictBaseline\.ps1$', + '^tools/Install-DLLPickleTestPowerShell\.ps1$', + '^src/DLLPickle/' ) $Relevant = $false $LiveValidation = $false @@ -109,7 +124,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: - dotnet-version: "8.0.x" + dotnet-version: "10.0.302" # This job runs only Analyze,Test (PowerShell) and performs no `dotnet restore`, so the # NuGet global-packages folder is never created. Enabling the setup-dotnet cache makes its # post-job save step fail ("Cache folder path ... doesn't exist on disk") on any cache miss @@ -125,96 +140,21 @@ jobs: shell: pwsh run: | $ErrorActionPreference = 'Stop' - Invoke-Build -File ./build/DLLPickle.Build.ps1 -Task Analyze,Test + ./tools/Invoke-DLLPickleBuild.ps1 -Task Analyze,Test - - name: Validate live upstream conflict-surface freshness + - name: Confirm exact-profile live validation routing if: needs.pr-changes.outputs.live_validation == 'true' shell: pwsh run: | - # Freshness check, NOT a bundle validation. Recompute the upstream module conflict surface - # whenever the policy, bundle dependency files, or fingerprint-producing tools change. - # - # This step does NOT validate the bumped bundle itself. A self-bump of DLLPickle's own NuGet - # references does not change what the upstream modules ship, so it will not move this - # fingerprint. The bumped bundle is validated separately by the "Build Module" workflow, which - # on every PR touching src/** runs the full build under --locked-mode plus the #193 / Azure.Core - # integration repro guards, bounded by the csproj floating-with-cap constraints. For auto-merge - # to actually wait on those signals, the Build Module and Dependency Review checks must be - # configured as required status checks (see Dependabot-Auto-Approve.yml). - $ErrorActionPreference = 'Stop' - ./tools/Get-DLLPickleUpstreamInventory.ps1 ` - -PolicyPath ./build/dependency-policy.json ` - -ModuleCachePath ./artifacts/upstreamCompatibility/modules ` - -OutputPath ./artifacts/upstreamCompatibility/upstream-inventory.json ` - -Force - ./tools/New-DLLPickleConflictMatrix.ps1 ` - -InventoryPath ./artifacts/upstreamCompatibility/upstream-inventory.json ` - -OutputPath ./artifacts/upstreamCompatibility/conflict-matrix.json | Out-Null - $policy = Get-Content ./build/dependency-policy.json -Raw | ConvertFrom-Json - $current = Get-Content ./artifacts/upstreamCompatibility/conflict-matrix.json -Raw | ConvertFrom-Json - $inventory = Get-Content ./artifacts/upstreamCompatibility/upstream-inventory.json -Raw | ConvertFrom-Json - $fingerprint = [string]$current.Fingerprint - $baseline = [string]$policy.baseline.conflictSurfaceFingerprint - $comparison = $null - if ($policy.baseline.PSObject.Properties.Name -contains 'conflictSurface') { - $baselineMatrix = [PSCustomObject]@{ - Assemblies = @($policy.baseline.conflictSurface | ForEach-Object { - [PSCustomObject]@{ - Name = [string]$_.name - Versions = @($_.versions) - ShippedBy = @($_.shippedBy) - Diverges = $true - AlcOwner = $null - } - }) - } - $comparison = ./tools/Compare-DLLPickleConflictMatrix.ps1 -Baseline $baselineMatrix -Current $current - } - - $report = [PSCustomObject]@{ - BaselineFingerprint = $baseline - CurrentFingerprint = $fingerprint - ModuleVersions = [ordered]@{} - Comparison = $comparison - } - foreach ($module in $inventory.Modules) { $report.ModuleVersions[[string]$module.Name] = [string]$module.Version } - $report | ConvertTo-Json -Depth 20 | Set-Content ./artifacts/upstreamCompatibility/comparison-report.json -Encoding utf8NoBOM - - @( - '# Upstream conflict-surface freshness' - '' - "- Baseline fingerprint: ``$baseline``" - "- Current fingerprint: ``$fingerprint``" - '' - '| Module | Version |' - '| --- | --- |' - ($inventory.Modules | Sort-Object Name | ForEach-Object { "| $($_.Name) | $($_.Version) |" }) - ) | Add-Content -LiteralPath $env:GITHUB_STEP_SUMMARY -Encoding utf8 - - if ($comparison -and $comparison.HasMaterialDrift) { - @( - '' - '## Structured drift' - '' - '```json' - ($comparison | ConvertTo-Json -Depth 20) - '```' - ) | Add-Content -LiteralPath $env:GITHUB_STEP_SUMMARY -Encoding utf8 - } - - if ($fingerprint -ne $baseline) { - Write-Host "::error::Upstream conflict surface changed (baseline '$baseline' -> '$fingerprint'). The preload/block decision this bundle is built against may be stale: re-adjudicate and update build/dependency-policy.json (classification + evidence + baseline) per docs/Architecture.md section 8 before merging." - throw 'Upstream conflict-surface drift detected; blocking merge until re-adjudicated.' - } - Write-Host "Upstream conflict surface unchanged (fingerprint '$fingerprint'); the recorded preload/block baseline still holds. (Bundle correctness is validated separately by the Build Module workflow.)" - + Write-Host 'Live upstream inventory and baseline comparison run in the exact nine-cell profile-evidence matrix.' + Write-Host 'Test-DLLPickleProfileConflictBaseline fails closed for missing, unaccepted, or drifted profile/OS baselines.' - name: Validate preload TFM alignment (Step 0b) if: needs.pr-changes.outputs.live_validation == 'true' shell: pwsh run: | # docs/Architecture.md section 8.2 Step 0(b): when a PR changes the bundled dependency set - # (csproj / packages.lock.json), prove each preload package ships a portable net8.0/ - # netstandard2.0 asset before it can be auto-merged. This runs on the PR gate that + # (csproj / packages.lock.json), prove NuGet selects a concrete assembly asset for every + # preload package under net8.0, net9.0, and net10.0. This runs on the PR gate that # Dependabot auto-merge waits on, so a bundle bump cannot merge on 0a (Build gate) alone. $ErrorActionPreference = 'Stop' dotnet restore ./src/DLLPickle.Build/DLLPickle.csproj --locked-mode @@ -224,13 +164,13 @@ jobs: -OutputPath ./artifacts/upstreamCompatibility/tfm-alignment.json ` -Strict @( - '# Preload TFM alignment (net8.0)' + '# Preload TFM alignment (all supported profiles)' '' "- Aligned: ``$($report.IsAligned)``" '' - '| Package | Version | Aligned | Compatible assets |' - '| --- | --- | --- | --- |' - ($report.Packages | ForEach-Object { "| $($_.PackageName) | $($_.ResolvedVersion) | $($_.IsAligned) | $((@($_.CompatibleAssets) -join ', ')) |" }) + '| TFM | Package | Version | Aligned | NuGet-selected assets |' + '| --- | --- | --- | --- | --- |' + ($report.Packages | ForEach-Object { "| $($_.TargetFramework) | $($_.PackageName) | $($_.ResolvedVersion) | $($_.IsAligned) | $((@($_.SelectedAssets) -join ', ')) |" }) ) | Add-Content -LiteralPath $env:GITHUB_STEP_SUMMARY -Encoding utf8 - name: Upload PR upstream compatibility evidence @@ -246,9 +186,322 @@ jobs: if-no-files-found: warn retention-days: 14 + profile-matrix: + name: Generate profile-aware upstream matrix + needs: pr-changes + if: ${{ always() && (github.event_name != 'pull_request' || needs.pr-changes.outputs.live_validation == 'true') }} + runs-on: ubuntu-24.04 + permissions: + contents: read + outputs: + matrix: ${{ steps.generate.outputs.matrix }} + steps: + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Generate exact profile matrix + id: generate + shell: pwsh + run: | + $Matrix = ./tools/New-DLLPicklePowerShellTestMatrix.ps1 -Compress + "matrix=$Matrix" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8 + + profile-evidence: + name: Upstream evidence ${{ matrix.powerShellVersion }} on ${{ matrix.platform }} + needs: profile-matrix + if: ${{ needs.profile-matrix.result == 'success' }} + runs-on: ${{ matrix.runner }} + timeout-minutes: 45 + permissions: + contents: read + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.profile-matrix.outputs.matrix) }} + steps: + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Setup .NET + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 + with: + dotnet-version: "10.0.302" + cache: true + cache-dependency-path: "src/DLLPickle.Build/packages.lock.json" + + - name: Cache exact stock PowerShell archive and payload + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ${{ runner.temp }}/dllpickle-test-powershell + key: upstream-${{ runner.os }}-${{ runner.arch }}-${{ matrix.provider }}-${{ matrix.powerShellVersion }}-${{ hashFiles('build/powershell-test-matrix.json', 'tools/Install-DLLPickleTestPowerShell.ps1') }} + + - name: Cache compatible upstream modules + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ${{ runner.temp }}/dllpickle-upstream-modules/${{ matrix.powerShellVersion }} + key: upstream-modules-${{ runner.os }}-${{ runner.arch }}-${{ matrix.powerShellVersion }}-${{ hashFiles('build/dependency-policy.json') }} + + - name: Cache isolated build modules + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ${{ runner.temp }}/dllpickle-upstream-build-modules + key: upstream-build-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('build/build-tool-versions.json', 'build/DLLPickle.Tooling.ps1') }} + + - name: Build the candidate DLLPickle module for differential scenarios + shell: pwsh + run: | + $BuildModuleRoot = '${{ runner.temp }}/dllpickle-upstream-build-modules' + ./.github/ci-scripts/Actions_Bootstrap.ps1 -ModuleInstallPath $BuildModuleRoot + ./tools/Invoke-DLLPickleBuild.ps1 -Task PrepareModuleOutput + + - name: Provision exact stock PowerShell + shell: pwsh + env: + MATRIX_PROVIDER: ${{ matrix.provider }} + MATRIX_POWERSHELL_VERSION: ${{ matrix.powerShellVersion }} + MATRIX_PLATFORM: ${{ matrix.platform }} + MATRIX_ARCHITECTURE: ${{ matrix.architecture }} + RUNTIME_INSTALL_ROOT: ${{ runner.temp }}/dllpickle-test-powershell + run: | + $InstallParameters = @{ + Provider = $env:MATRIX_PROVIDER + PowerShellVersion = $env:MATRIX_POWERSHELL_VERSION + Platform = $env:MATRIX_PLATFORM + Architecture = $env:MATRIX_ARCHITECTURE + InstallRoot = $env:RUNTIME_INSTALL_ROOT + PassThru = $true + } + $Identity = ./tools/Install-DLLPickleTestPowerShell.ps1 @InstallParameters + "DLLPICKLE_UPSTREAM_PWSH=$($Identity.ExecutablePath)" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 + + - name: Capture and validate profile-selected upstream evidence + shell: pwsh + env: + MATRIX_POWERSHELL_VERSION: ${{ matrix.powerShellVersion }} + MATRIX_TARGET_FRAMEWORK: ${{ matrix.targetFramework }} + MATRIX_PLATFORM: ${{ matrix.platform }} + MATRIX_ARCHITECTURE: ${{ matrix.architecture }} + PROFILE_MODULE_CACHE: ${{ runner.temp }}/dllpickle-upstream-modules/${{ matrix.powerShellVersion }} + SOURCE_RUN_ID: ${{ github.run_id }} + SOURCE_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + SOURCE_COMMIT_SHA: ${{ github.sha }} + run: | + $ErrorActionPreference = 'Stop' + $EvidenceRoot = Join-Path './artifacts/upstreamCompatibility' "$($env:MATRIX_POWERSHELL_VERSION)/$($env:MATRIX_PLATFORM)" + $ModuleCache = $env:PROFILE_MODULE_CACHE + $InventoryPath = Join-Path -Path $EvidenceRoot -ChildPath 'upstream-inventory.json' + $ConflictMatrixPath = Join-Path -Path $EvidenceRoot -ChildPath 'conflict-matrix.json' + $InventoryArguments = @( + '-NoLogo', '-NoProfile', '-NonInteractive', '-File', './tools/Get-DLLPickleUpstreamInventory.ps1' + '-PolicyPath', './build/dependency-policy.json' + '-TestMatrixPath', './build/powershell-test-matrix.json' + '-ModuleCachePath', $ModuleCache + '-OutputPath', $InventoryPath + '-PowerShellExecutable', $env:DLLPICKLE_UPSTREAM_PWSH + '-Force' + ) + & $env:DLLPICKLE_UPSTREAM_PWSH @InventoryArguments + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + ./tools/New-DLLPickleConflictMatrix.ps1 -InventoryPath $InventoryPath -OutputPath $ConflictMatrixPath | Out-Null + $ScenarioEvidencePath = Join-Path -Path $EvidenceRoot -ChildPath 'scenario-evidence.json' + $ScenarioParameters = @{ + PolicyPath = './build/dependency-policy.json' + InventoryPath = $InventoryPath + PowerShellExecutable = $env:DLLPICKLE_UPSTREAM_PWSH + DLLPickleManifestPath = './module/DLLPickle/DLLPickle.psd1' + KnownConflictsPath = './src/DLLPickle/KnownConflicts.json' + OutputPath = $ScenarioEvidencePath + Strict = $true + } + ./tools/New-DLLPickleUpstreamScenarioEvidence.ps1 @ScenarioParameters | Out-Null + + $Policy = Get-Content -LiteralPath ./build/dependency-policy.json -Raw | ConvertFrom-Json + $GapReport = [ordered]@{ + schemaVersion = 1 + profile = '{0}/{1}/{2}/{3}' -f $env:MATRIX_POWERSHELL_VERSION, $env:MATRIX_TARGET_FRAMEWORK, $env:MATRIX_PLATFORM, $env:MATRIX_ARCHITECTURE + deterministicImportNoAuth = 'executed-two-orders-with-and-without-dllpickle' + authenticatedReadOnly = 'not-run-no-approved-credentials' + writesPerformed = $false + unexecutedCommands = @($Policy.monitoredModules.authenticatedReadOnlyProbeCommand) + } + $ValidationGapsPath = Join-Path $EvidenceRoot 'validation-gaps.json' + $GapReport | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $ValidationGapsPath -Encoding utf8 + $NormalizedEvidencePath = Join-Path $EvidenceRoot 'normalized-profile-evidence.json' + $NormalizedEvidenceParameters = @{ + InventoryPath = $InventoryPath + ConflictMatrixPath = $ConflictMatrixPath + ScenarioEvidencePath = $ScenarioEvidencePath + ValidationGapsPath = $ValidationGapsPath + OutputPath = $NormalizedEvidencePath + SourceRunId = $env:SOURCE_RUN_ID + SourceRunUrl = $env:SOURCE_RUN_URL + SourceCommitSha = $env:SOURCE_COMMIT_SHA + } + ./tools/New-DLLPickleNormalizedProfileEvidence.ps1 @NormalizedEvidenceParameters | Out-Null + $BaselineParameters = @{ + PolicyPath = './build/dependency-policy.json' + ConflictMatrixPath = $ConflictMatrixPath + ScenarioEvidencePath = $ScenarioEvidencePath + NormalizedEvidencePath = $NormalizedEvidencePath + OutputPath = (Join-Path $EvidenceRoot 'baseline-comparison.json') + } + ./tools/Test-DLLPickleProfileConflictBaseline.ps1 @BaselineParameters + + - name: Upload profile-aware upstream evidence + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: upstream-${{ matrix.powerShellVersion }}-${{ matrix.platform }}-${{ matrix.architecture }} + path: ./artifacts/upstreamCompatibility/${{ matrix.powerShellVersion }}/${{ matrix.platform }} + if-no-files-found: error + retention-days: 14 + + profile-evidence-gate: + name: Aggregate profile-aware upstream evidence + needs: [pr-changes, profile-matrix, profile-evidence] + if: ${{ always() }} + runs-on: ubuntu-24.04 + steps: + - name: Report exact-profile evidence result + shell: pwsh + env: + EVENT_NAME: ${{ github.event_name }} + LIVE_VALIDATION: ${{ needs.pr-changes.outputs.live_validation }} + run: | + $MatrixResult = '${{ needs.profile-matrix.result }}' + $Result = '${{ needs.profile-evidence.result }}' + $ValidationRequired = $env:EVENT_NAME -ne 'pull_request' -or $env:LIVE_VALIDATION -eq 'true' + if ($ValidationRequired) { + if ($MatrixResult -ne 'success') { + throw "Required exact profile matrix generation did not succeed: $MatrixResult" + } + if ($Result -ne 'success') { + throw "Required profile-aware upstream evidence or baseline validation did not succeed: $Result" + } + } elseif ($MatrixResult -notin @('success', 'skipped') -or $Result -notin @('success', 'skipped')) { + throw "Optional profile validation ended unexpectedly: matrix=$MatrixResult, evidence=$Result" + } + Write-Host "Exact profile validation required: $ValidationRequired" + Write-Host "Exact profile matrix result: $MatrixResult" + Write-Host "Profile-aware upstream evidence result: $Result" + + scheduled-profile-evidence-report: + name: Summarize scheduled profile-aware evidence + needs: profile-evidence + if: ${{ always() && github.event_name != 'pull_request' }} + runs-on: ubuntu-24.04 + permissions: + contents: read + issues: write + steps: + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Download all available profile evidence + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + pattern: upstream-* + path: ./downloaded-upstream + merge-multiple: false + + - name: Build one stable profile-aware finding + shell: pwsh + run: | + $Parameters = @{ + EvidenceRoot = './downloaded-upstream' + OutputPath = './artifacts/upstreamCompatibility/profile-evidence-summary.json' + } + $Summary = ./tools/New-DLLPickleProfileEvidenceSummary.ps1 @Parameters + @( + '### Profile-aware upstream evidence' + '' + "- Complete profile set: $($Summary.AllProfileEvidencePresent)" + "- Ready for candidate updates: $($Summary.ReadyForCandidateUpdate)" + "- Findings: $(@($Summary.Findings).Count)" + "- Stable finding fingerprint: ``$($Summary.AggregateFindingFingerprint)``" + '- Publication: one issue comment per stable finding fingerprint' + ) | Add-Content -LiteralPath $env:GITHUB_STEP_SUMMARY + + - name: Publish one profile finding per stable fingerprint + if: ${{ hashFiles('artifacts/upstreamCompatibility/profile-evidence-summary.json') != '' }} + shell: pwsh + env: + GH_TOKEN: ${{ github.token }} + run: | + $ErrorActionPreference = 'Stop' + $Summary = Get-Content -LiteralPath './artifacts/upstreamCompatibility/profile-evidence-summary.json' -Raw | ConvertFrom-Json + if ($Summary.ReadyForCandidateUpdate) { + Write-Host 'All exact-profile baselines are accepted and unchanged; no finding will be published.' + exit 0 + } + + $Fingerprint = [string]$Summary.AggregateFindingFingerprint + if ($Fingerprint -notmatch '^[a-f0-9]{64}$') { + throw 'The profile evidence summary did not contain a stable SHA-256 finding fingerprint.' + } + $Title = 'Profile-aware upstream compatibility finding' + $Body = @( + 'The scheduled exact-profile compatibility sweep found evidence that requires maintainer review.' + '' + "Complete profile set: $($Summary.AllProfileEvidencePresent)" + "Missing profiles: $(@($Summary.MissingProfileKeys) -join ', ')" + "Unexpected profiles: $(@($Summary.UnexpectedProfileKeys) -join ', ')" + "Duplicate profiles: $(@($Summary.DuplicateProfileKeys) -join ', ')" + '' + 'Profile findings:' + $(if (@($Summary.Findings).Count -eq 0) { '- None' } else { @($Summary.Findings | Sort-Object ProfileKey | ForEach-Object { "- $($_.ProfileKey): $($_.Status); conflict $($_.BaselineFingerprint) -> $($_.CurrentFingerprint); scenario $($_.BaselineScenarioFingerprint) -> $($_.CurrentScenarioFingerprint); normalized evidence $($_.BaselineEvidenceFingerprint) -> $($_.CurrentEvidenceFingerprint)" }) }) + '' + "Workflow evidence: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + '' + [string]$Summary.FindingMarker + ) -join [System.Environment]::NewLine + + $Candidates = @(gh issue list --state all --search "$Title in:title" --limit 20 --json number,state,title | ConvertFrom-Json | Where-Object title -EQ $Title) + if ($LASTEXITCODE -ne 0) { throw 'Failed to query existing profile-aware compatibility issues.' } + $ExistingIssue = $Candidates | Select-Object -First 1 + $AlreadyReported = $false + if ($ExistingIssue) { + $ExistingContent = gh issue view $ExistingIssue.number --json body,comments | ConvertFrom-Json + if ($LASTEXITCODE -ne 0) { throw 'Failed to read the existing profile-aware compatibility issue.' } + $PublishedText = @($ExistingContent.body) + @($ExistingContent.comments.body) + $AlreadyReported = ./tools/Test-DLLPickleFindingFingerprintReported.ps1 -Fingerprint $Fingerprint -Text $PublishedText + } + + if ($AlreadyReported) { + Write-Host "Profile finding fingerprint $Fingerprint is already reported; suppressing a duplicate comment." + } elseif ($ExistingIssue) { + if ([string]$ExistingIssue.state -eq 'CLOSED') { + gh issue reopen $ExistingIssue.number + if ($LASTEXITCODE -ne 0) { throw 'Failed to reopen the profile-aware compatibility issue.' } + } + gh issue comment $ExistingIssue.number --body $Body + if ($LASTEXITCODE -ne 0) { throw 'Failed to publish the new profile-aware compatibility finding.' } + } else { + gh issue create --title $Title --body $Body + if ($LASTEXITCODE -ne 0) { throw 'Failed to open the profile-aware compatibility issue.' } + } + + - name: Fail closed when profile evidence requires review + if: ${{ hashFiles('artifacts/upstreamCompatibility/profile-evidence-summary.json') != '' }} + shell: pwsh + run: | + $Summary = Get-Content -LiteralPath './artifacts/upstreamCompatibility/profile-evidence-summary.json' -Raw | ConvertFrom-Json + if (-not $Summary.ReadyForCandidateUpdate) { + throw 'Profile-aware upstream evidence requires review; publication was deduplicated by its stable fingerprint.' + } + + - name: Upload the aggregate profile evidence report + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: upstream-profile-evidence-summary + path: ./artifacts/upstreamCompatibility/profile-evidence-summary.json + if-no-files-found: error + retention-days: 30 + pr-gate: name: Validate upstream compatibility tooling - needs: [pr-changes, pr-smoke-validation] + needs: [pr-changes, pr-smoke-validation, profile-evidence-gate] if: ${{ always() }} runs-on: ubuntu-latest steps: @@ -262,7 +515,9 @@ jobs: $DetectionResult = '${{ needs.pr-changes.result }}' $Relevant = '${{ needs.pr-changes.outputs.relevant }}' + $LiveValidation = '${{ needs.pr-changes.outputs.live_validation }}' $ValidationResult = '${{ needs.pr-smoke-validation.result }}' + $ProfileEvidenceResult = '${{ needs.profile-evidence-gate.result }}' if ($DetectionResult -ne 'success') { throw "Changed-file detection did not succeed: $DetectionResult" } @@ -272,27 +527,53 @@ jobs: if ($Relevant -eq 'false' -and $ValidationResult -notin @('skipped', 'success')) { throw "Irrelevant-path validation had an unexpected result: $ValidationResult" } - Write-Host "Upstream compatibility aggregate gate passed (relevant=$Relevant, validation=$ValidationResult)." + if ($LiveValidation -eq 'true' -and $ProfileEvidenceResult -ne 'success') { + throw "Profile-aware upstream evidence did not succeed: $ProfileEvidenceResult" + } + Write-Host "Upstream compatibility aggregate gate passed (relevant=$Relevant, validation=$ValidationResult, profileEvidence=$ProfileEvidenceResult)." scheduled-upstream-compatibility: name: Validate latest upstream module compatibility - if: github.event_name != 'pull_request' + needs: scheduled-profile-evidence-report + if: ${{ always() && github.event_name != 'pull_request' && needs.scheduled-profile-evidence-report.result == 'success' }} runs-on: windows-2025 permissions: + actions: read contents: write issues: write pull-requests: write steps: + - name: Create GitHub App token for candidate publication + id: app-token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + - name: Check out repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 + token: ${{ steps.app-token.outputs.token }} + + - name: Download accepted exact-profile evidence summary + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: upstream-profile-evidence-summary + path: ./downloaded-profile-summary + + - name: Download exact-profile inventories + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + pattern: upstream-*-*-x64 + path: ./downloaded-profile-evidence + merge-multiple: false - name: Setup .NET uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: - dotnet-version: "8.0.x" + dotnet-version: "10.0.302" cache: true cache-dependency-path: "src/DLLPickle.Build/packages.lock.json" @@ -300,21 +581,46 @@ jobs: shell: pwsh run: ./.github/ci-scripts/Actions_Bootstrap.ps1 + - name: Provision exact stock PowerShell for candidate validation + shell: pwsh + run: | + $Matrix = Get-Content -LiteralPath './build/powershell-test-matrix.json' -Raw | ConvertFrom-Json + $Profile = @($Matrix.profiles | Where-Object { + $_.powerShellMajor -eq 7 -and $_.powerShellMinor -eq 6 -and $_.targetFramework -eq 'net10.0' + }) + if ($Profile.Count -ne 1) { + throw 'Expected exactly one PowerShell 7.6/net10.0 profile in the test matrix.' + } + $InstallParameters = @{ + Provider = 'DirectArchive' + PowerShellVersion = [string]$Profile[0].powerShellVersion + Platform = 'windows' + Architecture = 'x64' + InstallRoot = '${{ runner.temp }}/dllpickle-test-powershell' + PassThru = $true + } + $Identity = ./tools/Install-DLLPickleTestPowerShell.ps1 @InstallParameters + "DLLPICKLE_UPSTREAM_PWSH=$($Identity.ExecutablePath)" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 + - name: Inventory latest upstream modules shell: pwsh run: | $ErrorActionPreference = 'Stop' - ./tools/Get-DLLPickleUpstreamInventory.ps1 ` - -PolicyPath ./build/dependency-policy.json ` - -ModuleCachePath ./artifacts/upstreamCompatibility/modules ` - -OutputPath ./artifacts/upstreamCompatibility/upstream-inventory.json ` - -Force + $InventoryParameters = @{ + PolicyPath = './build/dependency-policy.json' + TestMatrixPath = './build/powershell-test-matrix.json' + ModuleCachePath = './artifacts/upstreamCompatibility/modules' + OutputPath = './artifacts/upstreamCompatibility/upstream-inventory.json' + PowerShellExecutable = $env:DLLPICKLE_UPSTREAM_PWSH + Force = $true + } + ./tools/Get-DLLPickleUpstreamInventory.ps1 @InventoryParameters - name: Detect conflict-surface drift id: drift shell: pwsh env: - GH_TOKEN: ${{ github.token }} + GH_TOKEN: ${{ steps.app-token.outputs.token }} run: | $ErrorActionPreference = 'Stop' ./tools/New-DLLPickleConflictMatrix.ps1 ` @@ -324,31 +630,32 @@ jobs: $current = Get-Content ./artifacts/upstreamCompatibility/conflict-matrix.json -Raw | ConvertFrom-Json $inventory = Get-Content ./artifacts/upstreamCompatibility/upstream-inventory.json -Raw | ConvertFrom-Json $surface = @($current.ConflictSurface | Sort-Object) - # Versions-aware fingerprint produced by New-DLLPickleConflictMatrix (single source of the - # algorithm) so a within-major version move on a still-conflicting assembly is detected, - # not just name-set changes. (ALC-ownership drift is the runtime-probe / maintainer tier.) + # Versions-aware fingerprint produced by New-DLLPickleConflictMatrix. This final sanity + # check is profile-specific; the nine-cell aggregate job has already proved that all + # accepted profile baselines are unchanged before candidate automation can reach this job. $fingerprint = [string]$current.Fingerprint - $baseline = [string]$policy.baseline.conflictSurfaceFingerprint - $comparison = $null - if ($policy.baseline.PSObject.Properties.Name -contains 'conflictSurface') { - $baselineMatrix = [PSCustomObject]@{ - Assemblies = @($policy.baseline.conflictSurface | ForEach-Object { - [PSCustomObject]@{ - Name = [string]$_.name - Versions = @($_.versions) - ShippedBy = @($_.shippedBy) - Diverges = $true - AlcOwner = $null - } - }) - } - $comparison = ./tools/Compare-DLLPickleConflictMatrix.ps1 -Baseline $baselineMatrix -Current $current + $ProfilePolicy = @($policy.runtimeProfiles | Where-Object { + $_.powerShellLine -eq '7.6' -and $_.targetFramework -eq 'net10.0' + }) + if ($ProfilePolicy.Count -ne 1) { + throw 'Expected exactly one 7.6/net10.0 profile policy for scheduled candidate generation.' + } + $ProfileBaseline = $ProfilePolicy[0].baselines.windows + if ([string]$ProfileBaseline.status -ne 'accepted') { + throw "The 7.6/net10.0/windows baseline is not accepted (status '$($ProfileBaseline.status)')." } + $baseline = [string]$ProfileBaseline.conflictSurfaceFingerprint + $ProfileKey = 'ps7.6-net10.0-windows-x64' + $FindingText = "$ProfileKey|$baseline|$fingerprint" + $FindingBytes = [System.Text.Encoding]::UTF8.GetBytes($FindingText) + $FindingFingerprint = [System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::HashData($FindingBytes)).Replace('-', '').ToLowerInvariant() + $FindingMarker = '' -f $FindingFingerprint $report = [PSCustomObject]@{ + ProfileKey = $ProfileKey BaselineFingerprint = $baseline CurrentFingerprint = $fingerprint + FindingFingerprint = $FindingFingerprint ModuleVersions = [ordered]@{} - Comparison = $comparison } foreach ($module in $inventory.Modules) { $report.ModuleVersions[[string]$module.Name] = [string]$module.Version } $report | ConvertTo-Json -Depth 20 | Set-Content ./artifacts/upstreamCompatibility/comparison-report.json -Encoding utf8NoBOM @@ -383,10 +690,32 @@ jobs: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}' '' 'Re-run the runtime ALC adjudication and update build/dependency-policy.json (classification + evidence + baseline) per docs/Architecture.md section 8.' + '' + $FindingMarker ) -join [System.Environment]::NewLine - $Existing = gh issue list --state open --search "$Title in:title" --json number --jq '.[0].number' - if ([string]::IsNullOrWhiteSpace($Existing)) { gh issue create --title $Title --body $Body } - else { gh issue comment $Existing --body $Body } + $Candidates = @(gh issue list --state all --search "$Title in:title" --limit 20 --json number,state,title | ConvertFrom-Json | Where-Object title -EQ $Title) + if ($LASTEXITCODE -ne 0) { throw 'Failed to query existing conflict-surface drift issues.' } + $ExistingIssue = $Candidates | Select-Object -First 1 + $AlreadyReported = $false + if ($ExistingIssue) { + $ExistingContent = gh issue view $ExistingIssue.number --json body,comments | ConvertFrom-Json + if ($LASTEXITCODE -ne 0) { throw 'Failed to read the existing conflict-surface drift issue.' } + $PublishedText = @($ExistingContent.body) + @($ExistingContent.comments.body) + $AlreadyReported = ./tools/Test-DLLPickleFindingFingerprintReported.ps1 -Fingerprint $FindingFingerprint -Text $PublishedText + } + if ($AlreadyReported) { + Write-Host "Conflict finding fingerprint $FindingFingerprint is already reported; suppressing a duplicate comment." + } elseif ($ExistingIssue) { + if ([string]$ExistingIssue.state -eq 'CLOSED') { + gh issue reopen $ExistingIssue.number + if ($LASTEXITCODE -ne 0) { throw 'Failed to reopen the conflict-surface drift issue.' } + } + gh issue comment $ExistingIssue.number --body $Body + if ($LASTEXITCODE -ne 0) { throw 'Failed to publish the new conflict-surface drift finding.' } + } else { + gh issue create --title $Title --body $Body + if ($LASTEXITCODE -ne 0) { throw 'Failed to open the conflict-surface drift issue.' } + } } else { Write-Host "Conflict surface unchanged (fingerprint '$fingerprint')." "drift=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8 @@ -397,12 +726,21 @@ jobs: shell: pwsh run: | $ErrorActionPreference = 'Stop' - ./tools/Update-DLLPickleDependencyPins.ps1 ` - -InventoryPath ./artifacts/upstreamCompatibility/upstream-inventory.json ` - -PolicyPath ./build/dependency-policy.json ` - -ProjectPath ./src/DLLPickle.Build/DLLPickle.csproj ` - -OutputPath ./artifacts/upstreamCompatibility/candidate-report.json ` - -Restore + $InventoryPaths = @( + Get-ChildItem -LiteralPath './downloaded-profile-evidence' -Filter 'upstream-inventory.json' -File -Recurse | + Select-Object -ExpandProperty FullName + ) + if ($InventoryPaths.Count -ne 9) { + throw "Candidate pin generation requires exactly nine profile inventories; found $($InventoryPaths.Count)." + } + $UpdateParameters = @{ + InventoryPath = $InventoryPaths + PolicyPath = './build/dependency-policy.json' + ProjectPath = './src/DLLPickle.Build/DLLPickle.csproj' + OutputPath = './artifacts/upstreamCompatibility/candidate-report.json' + Restore = $true + } + ./tools/Update-DLLPickleDependencyPins.ps1 @UpdateParameters - name: Validate candidate update if: steps.drift.outputs.drift != 'true' @@ -410,7 +748,7 @@ jobs: run: | $ErrorActionPreference = 'Stop' dotnet restore ./src/DLLPickle.Build/DLLPickle.csproj --locked-mode - Invoke-Build -File ./build/DLLPickle.Build.ps1 -Task IssueReproTest + ./tools/Invoke-DLLPickleBuild.ps1 -Task IssueReproTest - name: Inspect preload TFM alignment (Step 0b) if: steps.drift.outputs.drift != 'true' @@ -418,9 +756,9 @@ jobs: run: | # docs/Architecture.md section 8.2 Step 0(b): the explicit half of "TFM alignment". The # Build gate (0a) proves restore+build green under --locked-mode; this proves each preload - # package actually ships a net8.0/netstandard2.0-compatible asset (not just transitive luck). - # The preceding --locked-mode restore populated the NuGet global-packages folder, so the - # tool can inspect each resolved package's lib// assets. Fail-closed: a misaligned + # package receives an actual resolved assembly asset in every supported target graph. + # The preceding --locked-mode restore generated project.assets.json, which is the authority + # for NuGet asset selection. Fail-closed: a missing selection # candidate must not proceed on the automated path. $ErrorActionPreference = 'Stop' $report = ./tools/Test-DLLPickleTfmAlignment.ps1 ` @@ -428,19 +766,19 @@ jobs: -LockFilePath ./src/DLLPickle.Build/packages.lock.json ` -OutputPath ./artifacts/upstreamCompatibility/tfm-alignment.json @( - '# Preload TFM alignment (net8.0)' + '# Preload TFM alignment (all supported profiles)' '' "- Aligned: ``$($report.IsAligned)``" '' - '| Package | Version | Aligned | Compatible assets |' - '| --- | --- | --- | --- |' - ($report.Packages | ForEach-Object { "| $($_.PackageName) | $($_.ResolvedVersion) | $($_.IsAligned) | $((@($_.CompatibleAssets) -join ', ')) |" }) + '| TFM | Package | Version | Aligned | NuGet-selected assets |' + '| --- | --- | --- | --- | --- |' + ($report.Packages | ForEach-Object { "| $($_.TargetFramework) | $($_.PackageName) | $($_.ResolvedVersion) | $($_.IsAligned) | $((@($_.SelectedAssets) -join ', ')) |" }) ) | Add-Content -LiteralPath $env:GITHUB_STEP_SUMMARY -Encoding utf8 if (-not $report.IsAligned) { - Write-Host "::error::Preload TFM alignment failed for: $((@($report.Misaligned)) -join ', '). A candidate that fails the explicit net8.0/netstandard2.0 asset check (docs/Architecture.md section 8.2 Step 0b) must not proceed." + Write-Host "::error::Preload TFM alignment failed for: $((@($report.Misaligned)) -join ', '). A candidate missing a NuGet-selected asset in any supported TFM must not proceed." throw 'TFM alignment check failed; blocking candidate generation.' } - Write-Host "Preload TFM alignment passed: every preload package ships a net8.0-compatible asset." + Write-Host "Preload TFM alignment passed: every preload package has a NuGet-selected asset in every supported TFM." - name: Detect generated changes if: steps.drift.outputs.drift != 'true' @@ -449,27 +787,63 @@ jobs: run: | $ErrorActionPreference = 'Stop' $Changed = -not [string]::IsNullOrWhiteSpace((git status --short -- src/DLLPickle.Build/DLLPickle.csproj src/DLLPickle.Build/packages.lock.json)) + $ProfileSummary = Get-Content -LiteralPath './downloaded-profile-summary/profile-evidence-summary.json' -Raw | ConvertFrom-Json + if (-not $ProfileSummary.ReadyForCandidateUpdate) { + throw 'Candidate publication requires a complete, accepted, unchanged exact-profile evidence summary.' + } + $CanonicalLines = [System.Collections.Generic.List[string]]::new() + $CanonicalLines.Add("profile-evidence|$($ProfileSummary.AggregateFindingFingerprint)") + foreach ($Path in @('src/DLLPickle.Build/DLLPickle.csproj', 'src/DLLPickle.Build/packages.lock.json')) { + $Hash = (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant() + $CanonicalLines.Add("candidate-file|$Path|$Hash") + } + $FingerprintBytes = [System.Text.Encoding]::UTF8.GetBytes((@($CanonicalLines | Sort-Object) -join [char]10)) + $PublicationFingerprint = [System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::HashData($FingerprintBytes)).Replace('-', '').ToLowerInvariant() "has_changes=$($Changed.ToString().ToLowerInvariant())" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8 + "publication_fingerprint=$PublicationFingerprint" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8 - name: Create candidate pull request if: steps.drift.outputs.drift != 'true' && steps.generated_changes.outputs.has_changes == 'true' && (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.create_pull_request)) shell: pwsh env: - GH_TOKEN: ${{ github.token }} + GH_TOKEN: ${{ steps.app-token.outputs.token }} run: | $ErrorActionPreference = 'Stop' - $BranchName = 'automation/upstream-compatibility-${{ github.run_id }}' + $Fingerprint = '${{ steps.generated_changes.outputs.publication_fingerprint }}' + if ($Fingerprint -notmatch '^[a-f0-9]{64}$') { + throw 'Candidate publication did not receive a stable SHA-256 fingerprint.' + } + $BranchName = "automation/upstream-compatibility-$($Fingerprint.Substring(0, 16))" + $ExistingPullRequests = @(gh pr list --state all --head $BranchName --limit 10 --json number,state,url,body | ConvertFrom-Json) + if ($LASTEXITCODE -ne 0) { throw 'Failed to query existing upstream candidate pull requests.' } + if ($ExistingPullRequests.Count -gt 0) { + Write-Host "Candidate fingerprint $Fingerprint is already published as $($ExistingPullRequests[0].url); suppressing a duplicate pull request." + exit 0 + } + git config user.name 'github-actions[bot]' git config user.email '41898282+github-actions[bot]@users.noreply.github.com' git switch -c $BranchName git add src/DLLPickle.Build/DLLPickle.csproj src/DLLPickle.Build/packages.lock.json git commit -m 'deps: update upstream compatibility pins' + if ($LASTEXITCODE -ne 0) { throw 'Failed to commit the validated upstream candidate.' } git push --set-upstream origin $BranchName - gh pr create ` - --title 'deps: update upstream compatibility pins' ` - --body 'Automated upstream compatibility validation detected a safe candidate dependency pin update. The workflow regenerated the NuGet lock file and validated the issue reproduction suite before opening this PR.' ` - --base main ` - --head $BranchName + if ($LASTEXITCODE -ne 0) { throw 'Failed to publish the validated upstream candidate branch.' } + + $Body = @( + 'Automated upstream compatibility validation detected a safe candidate dependency pin update.' + '' + '- All nine accepted exact-profile fingerprints were complete and unchanged.' + '- The workflow regenerated the NuGet lock file.' + '- Restore, issue-reproduction tests, and per-TFM asset alignment passed before publication.' + '' + "Validation run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + '' + "" + ) -join [System.Environment]::NewLine + $Arguments = @('pr', 'create', '--title', 'deps: update upstream compatibility pins', '--body', $Body, '--base', 'main', '--head', $BranchName) + & gh @Arguments + if ($LASTEXITCODE -ne 0) { throw 'Failed to open the validated upstream candidate pull request.' } - name: Upload upstream compatibility artifacts if: always() @@ -491,18 +865,60 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | + $ErrorActionPreference = 'Stop' $Title = 'Upstream compatibility automation failed' + $FailedSurfaces = [System.Collections.Generic.List[string]]::new() + $RunJobsJson = gh api "repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/jobs?filter=latest&per_page=100" + if ($LASTEXITCODE -eq 0) { + $RunJobs = $RunJobsJson | ConvertFrom-Json + foreach ($RunJob in @($RunJobs.jobs)) { + foreach ($RunStep in @($RunJob.steps | Where-Object conclusion -EQ 'failure')) { + $FailedSurfaces.Add("$($RunJob.name)|$($RunStep.number)|$($RunStep.name)") + } + } + } + if ($FailedSurfaces.Count -eq 0) { + $FailedSurfaces.Add('${{ github.job }}|unresolved-failure-surface') + } + $FailureCanonicalText = @($FailedSurfaces | Sort-Object) -join [char]10 + $FailureBytes = [System.Text.Encoding]::UTF8.GetBytes($FailureCanonicalText) + $FailureFingerprint = [System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::HashData($FailureBytes)).Replace('-', '').ToLowerInvariant() + $FailureMarker = '' -f $FailureFingerprint $Body = @( 'The scheduled upstream compatibility workflow failed.' '' + 'Failed job/step surface:' + @($FailedSurfaces | Sort-Object | ForEach-Object { "- $_" }) + '' 'Review the workflow run and uploaded artifacts for details:' '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}' '' 'This workflow is fail-closed. It does not publish or merge dependency preload changes unless inventory, candidate generation, restore, build, and issue repro validation all pass.' + '' + $FailureMarker ) -join [System.Environment]::NewLine - $ExistingIssue = gh issue list --state open --search "$Title in:title" --json number --jq '.[0].number' - if ([string]::IsNullOrWhiteSpace($ExistingIssue)) { - gh issue create --title $Title --body $Body + + $Candidates = @(gh issue list --state all --search "$Title in:title" --limit 20 --json number,state,title | ConvertFrom-Json | Where-Object title -EQ $Title) + if ($LASTEXITCODE -ne 0) { throw 'Failed to query existing upstream automation failure issues.' } + $ExistingIssue = $Candidates | Select-Object -First 1 + $AlreadyReported = $false + if ($ExistingIssue) { + $ExistingContent = gh issue view $ExistingIssue.number --json body,comments | ConvertFrom-Json + if ($LASTEXITCODE -ne 0) { throw 'Failed to read the existing upstream automation failure issue.' } + $PublishedText = @($ExistingContent.body) + @($ExistingContent.comments.body) + $AlreadyReported = ./tools/Test-DLLPickleFindingFingerprintReported.ps1 -Fingerprint $FailureFingerprint -Text $PublishedText + } + + if ($AlreadyReported) { + Write-Host "Failure fingerprint $FailureFingerprint is already reported; suppressing a duplicate comment." + } elseif ($ExistingIssue) { + if ([string]$ExistingIssue.state -eq 'CLOSED') { + gh issue reopen $ExistingIssue.number + if ($LASTEXITCODE -ne 0) { throw 'Failed to reopen the upstream automation failure issue.' } + } + gh issue comment $ExistingIssue.number --body $Body + if ($LASTEXITCODE -ne 0) { throw 'Failed to publish the new upstream automation failure finding.' } } else { - gh issue comment $ExistingIssue --body $Body + gh issue create --title $Title --body $Body + if ($LASTEXITCODE -ne 0) { throw 'Failed to open the upstream automation failure issue.' } } diff --git a/.github/workflows/Validate-Packages.yml b/.github/workflows/Validate-Packages.yml index f00e5a92..2fd8628e 100644 --- a/.github/workflows/Validate-Packages.yml +++ b/.github/workflows/Validate-Packages.yml @@ -35,7 +35,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: - dotnet-version: '8.0.x' + dotnet-version: '10.0.302' cache: true cache-dependency-path: 'src/DLLPickle.Build/packages.lock.json' @@ -46,3 +46,20 @@ jobs: - name: Build shell: pwsh run: dotnet build src/DLLPickle.Build/DLLPickle.csproj --configuration Release --no-restore + + - name: Verify all target-framework assets selected by NuGet + shell: pwsh + run: | + $Parameters = @{ + OutputPath = './artifacts/package/tfm-alignment.json' + Strict = $true + } + ./tools/Test-DLLPickleTfmAlignment.ps1 @Parameters + + - name: Upload TFM alignment report + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: package-tfm-alignment + path: ./artifacts/package/tfm-alignment.json + if-no-files-found: warn diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d04b87a..517c6271 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Documented the manual release dispatch process trap: Architecture §8.3 now spells out which changes auto-publish, which do not, and when a deliberate `workflow_dispatch` run is required, backed by new `Release-and-Publish` path/version-gate guardrail tests (GAP-006). - Documented the unresolved review-thread maintenance workflow so review threads are not resolved as a substitute for code, test, or documentation changes (GAP-010). - Added a structural gap-register guard (`tests/Unit/GapRegister.Tests.ps1`) that validates gap status values, index membership, index/frontmatter status agreement, and `resolution_pr`/`resolved_on` on resolved gaps; surfaced and fixed GAP-001's missing index row (GAP-011). +- Multi-targeted the shipped dependency payload for the exact Microsoft-supported PowerShell profiles currently in scope: 7.4 / `net8.0`, 7.5 / `net9.0`, and 7.6 / `net10.0`. The loader now selects with both PowerShell and CLR versions and fails closed on unknown, malformed, duplicate, or mismatched mappings. +- Added a canonical exact-patch/OS test matrix, checksum-verified stock-host provisioning, scheduled lifecycle enforcement, and a stable aggregate gate spanning all nine PowerShell/OS cells. Optional `multi-pwsh` provisioning remains CI-only and is explicitly rejected from the published artifact. +- Added fingerprint-deduplicated scheduled publication: validated patch-only runtime updates open one automation PR, while new/changed/retiring support lines and profile-aware compatibility findings update one review issue only when their stable evidence fingerprint changes. +- Made upstream inventory, conflict fingerprints, import-order evidence, probe commands, and baselines profile/platform aware. Deterministic no-auth and credential-dependent authenticated read-only tiers are reported separately; unexecuted credentialed probes remain release gates. +- Derived TFM compatibility from NuGet's restored `project.assets.json`, preserved common package versions across all TFMs by default, and routed conditional pins, classification changes, or material artifact growth to maintainer review. +- Added generated Microsoft support and upstream-evidence documentation, exact package-composition inspection, and deterministic per-TFM/full-artifact size reporting with a committed baseline. ## [2.2.2] - 2026-06-22 diff --git a/README.md b/README.md index 768b2322..e1391836 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ A PowerShell module that helps you get un-stuck from dependency version conflict ### Prerequisites -PowerShell 7.4 or later on Linux, macOS, or Windows. +One of the Microsoft-supported PowerShell lines in the [generated support matrix](docs/generated/Support-Matrix.md) on Linux, macOS, or Windows. The current contract is PowerShell 7.4, 7.5, and 7.6, each with its matching .NET runtime bundle. ### Installation @@ -57,7 +57,7 @@ For diagnostic detail, add `-ShowLoaderExceptions -Verbose`. - `Get-ModulesWithDependency` — list installed modules that package a given dependency. - `Get-ModulesWithVersionSortedIdentityClient` — compare modules by packaged `Microsoft.Identity.Client.dll` version. -> The inspection helpers are **cross-edition**. `Import-DPLibrary` needs PowerShell 7.4+, but these helpers also scan the Windows PowerShell module roots — so a **Windows PowerShell 5.1** user can run them to find which module to load first and apply the "first one wins" fix manually. +> The inspection helpers are **cross-edition**. `Import-DPLibrary` needs a supported PowerShell/.NET profile, but these helpers also scan the Windows PowerShell module roots — so a **Windows PowerShell 5.1** user can use a supported PowerShell session to find which module to load first and apply the "first one wins" fix manually. Full syntax and examples: [docs index](docs/index.md) · [command reference](docs/DLLPickle.md). @@ -65,9 +65,13 @@ Full syntax and examples: [docs index](docs/index.md) · [command reference](doc Many PowerShell modules — Az, Exchange Online, Microsoft Graph, Teams, and more — bundle their own copy of the Microsoft Authentication Library (MSAL) and related DLLs. A single PowerShell session can only load **one** version of a given DLL, so when two modules ship different versions you hit *"an assembly with the same name is already loaded"* and authentication breaks. -DLL Pickle preloads a current, compatible set of these assemblies **first**, so the "first one wins" rule works in your favor and the modules you load afterward reuse what's already there. A new DLL Pickle release is published automatically whenever a new MSAL version ships — so keep it updated and load it first. +DLL Pickle preloads a current, compatible set of these assemblies **first**, so the "first one wins" rule works in your favor and the modules you load afterward reuse what's already there. Dependency updates are locked, tested across every supported PowerShell/OS profile, and released only after the compatibility gates pass. -For the full explanation (and the real-world issues that motivated it), read the [Deep Dive](docs/Deep-Dive.md). The supported platform is **PowerShell 7.4+ (Core, net8.0)**. Compatibility, versioning, and dependency details live in [DEPENDENCIES.md](docs/DEPENDENCIES.md). +The loader selects `net8.0`, `net9.0`, or `net10.0` from the exact PowerShell minor and CLR major and fails closed on an unknown or mismatched pair. + +For the full explanation (and the real-world issues that motivated it), read the [Deep Dive](docs/Deep-Dive.md). + +Compatibility, versioning, and dependency details live in [DEPENDENCIES.md](docs/DEPENDENCIES.md). ## 📚 Documentation Map @@ -78,5 +82,7 @@ For the full explanation (and the real-world issues that motivated it), read the - Changelog and active work: [CHANGELOG.md](CHANGELOG.md) - Architecture blueprint and planned enhancements: [docs/Architecture.md](docs/Architecture.md) - Dependency, versioning, and supply-chain policy: [DEPENDENCIES.md](docs/DEPENDENCIES.md) +- Generated Microsoft support contract: [Support Matrix](docs/generated/Support-Matrix.md) +- Generated upstream evidence and explicit gaps: [Compatibility Evidence](docs/generated/Compatibility-Evidence.md) - Contribution workflow: [.github/CONTRIBUTING.md](.github/CONTRIBUTING.md) - Security vulnerability reporting: [SECURITY.md](SECURITY.md) diff --git a/build/DLLPickle.Build.ps1 b/build/DLLPickle.Build.ps1 index 825776f3..31ef1a3a 100644 --- a/build/DLLPickle.Build.ps1 +++ b/build/DLLPickle.Build.ps1 @@ -43,6 +43,14 @@ ### QUESTION: Is the $BuildFile variable created by Invoke-Build automatically? $script:ModuleName = [regex]::Match((Get-Item $BuildFile).Name, '^(.*)\.Build\.ps1$').Groups[1].Value . "$(Join-Path -Path $PSScriptRoot -ChildPath "${ModuleName}.Settings.ps1")" +$script:BuildToolPolicyPath = Join-Path -Path $PSScriptRoot -ChildPath 'build-tool-versions.json' +. (Join-Path -Path $PSScriptRoot -ChildPath 'DLLPickle.Tooling.ps1') +$script:BuildToolPolicy = Get-DLLPickleBuildToolPolicy -Path $script:BuildToolPolicyPath +$script:RequiredInvokeBuildVersion = Get-DLLPickleBuildToolVersion -Policy $script:BuildToolPolicy -Name 'InvokeBuild' +$script:RequiredPesterVersion = Get-DLLPickleBuildToolVersion -Policy $script:BuildToolPolicy -Name 'Pester' +$script:RequiredPSScriptAnalyzerVersion = Get-DLLPickleBuildToolVersion -Policy $script:BuildToolPolicy -Name 'PSScriptAnalyzer' +$script:RequiredPlatyPSVersion = Get-DLLPickleBuildToolVersion -Policy $script:BuildToolPolicy -Name 'Microsoft.PowerShell.PlatyPS' +$null = Import-DLLPickleBuildTool -Name 'InvokeBuild' -RequiredVersion $script:RequiredInvokeBuildVersion function Test-ManifestBool ($Path) { # Validate the module manifest file @@ -127,8 +135,6 @@ Enter-Build { '*[\\/]Private[\\/]Show-DLLPickleLogo.ps1' ) - [version]$script:MinPesterVersion = '5.2.2' - [version]$script:MaxPesterVersion = '5.99.99' $script:TestOutputFormat = 'NUnitXML' } #Enter-Build @@ -231,6 +237,7 @@ Add-BuildTask Clean { #Synopsis: Invoke PSScriptAnalyzer against the Module source path Add-BuildTask Analyze { + $null = Import-DLLPickleBuildTool -Name 'PSScriptAnalyzer' -RequiredVersion $script:RequiredPSScriptAnalyzerVersion $ScriptAnalyzerParams = @{ Path = $script:ModuleSourcePath Setting = 'PSScriptAnalyzerSettings.psd1' @@ -251,6 +258,7 @@ Add-BuildTask Analyze { #Synopsis: Invoke Script Analyzer against the Tests path if it exists Add-BuildTask AnalyzeTests -After Analyze { if (Test-Path -Path $script:TestsPath) { + $null = Import-DLLPickleBuildTool -Name 'PSScriptAnalyzer' -RequiredVersion $script:RequiredPSScriptAnalyzerVersion $ScriptAnalyzerParams = @{ Path = $script:TestsPath Setting = 'PSScriptAnalyzerSettings.psd1' @@ -273,6 +281,7 @@ Add-BuildTask AnalyzeTests -After Analyze { #Synopsis: Invoke Script Analyzer against repository maintenance tools if they exist Add-BuildTask AnalyzeTools -After AnalyzeTests { if (Test-Path -Path $script:ToolsPath) { + $null = Import-DLLPickleBuildTool -Name 'PSScriptAnalyzer' -RequiredVersion $script:RequiredPSScriptAnalyzerVersion $ScriptAnalyzerParams = @{ Path = $script:ToolsPath Setting = 'PSScriptAnalyzerSettings.psd1' @@ -293,6 +302,7 @@ Add-BuildTask AnalyzeTools -After AnalyzeTests { #Synopsis: Analyze scripts to verify if they adhere to desired coding format (Stroustrup / OTBS / Allman) Add-BuildTask FormattingCheck { + $null = Import-DLLPickleBuildTool -Name 'PSScriptAnalyzer' -RequiredVersion $script:RequiredPSScriptAnalyzerVersion $ScriptAnalyzerParams = @{ Setting = 'CodeFormattingOTBS' ExcludeRule = 'PSUseConsistentWhitespace' @@ -312,10 +322,8 @@ Add-BuildTask FormattingCheck { #Synopsis: Invoke all Pester Unit Tests in the Tests\Unit folder (if it exists) Add-BuildTask Test { - - Write-Build White " Importing desired Pester version. Min: $script:MinPesterVersion Max: $script:MaxPesterVersion" - Remove-Module -Name Pester -Force -ErrorAction 'SilentlyContinue' # there are instances where some containers have Pester already in the session - Import-Module -Name Pester -MinimumVersion $script:MinPesterVersion -MaximumVersion $script:MaxPesterVersion -ErrorAction 'Stop' + Write-Build White " Importing exact Pester version: $script:RequiredPesterVersion" + $null = Import-DLLPickleBuildTool -Name 'Pester' -RequiredVersion $script:RequiredPesterVersion $CodeCovPath = Join-Path -Path $script:ArtifactsPath -ChildPath 'ccReport' $TestOutputPath = Join-Path -Path $script:ArtifactsPath -ChildPath 'testOutput' @@ -389,9 +397,8 @@ Add-BuildTask Test { #Synopsis: Used primarily during active development to generate XML file to graphically display code coverage in VSCode using Coverage Gutters Add-BuildTask DevCC { Write-Build White ' Generating code coverage report at root...' - Write-Build White " Importing desired Pester version. Min: $script:MinPesterVersion Max: $script:MaxPesterVersion" - Remove-Module -Name Pester -Force -ErrorAction SilentlyContinue # there are instances where some containers have Pester already in the session - Import-Module -Name Pester -MinimumVersion $script:MinPesterVersion -MaximumVersion $script:MaxPesterVersion -ErrorAction 'Stop' + Write-Build White " Importing exact Pester version: $script:RequiredPesterVersion" + $null = Import-DLLPickleBuildTool -Name 'Pester' -RequiredVersion $script:RequiredPesterVersion $PesterConfiguration = New-PesterConfiguration $PesterConfiguration.run.Path = $script:UnitTestsPath $PesterConfiguration.CodeCoverage.Enabled = $true @@ -413,8 +420,8 @@ Add-BuildTask DevCC { Add-BuildTask CreateHelpStart { Write-Build White ' Performing all help related actions.' - Write-Build Gray ' Importing Microsoft.PowerShell.PlatyPS ...' - Import-Module Microsoft.PowerShell.PlatyPS -ErrorAction Stop + Write-Build Gray " Importing Microsoft.PowerShell.PlatyPS $script:RequiredPlatyPSVersion ..." + $null = Import-DLLPickleBuildTool -Name 'Microsoft.PowerShell.PlatyPS' -RequiredVersion $script:RequiredPlatyPSVersion Write-Build Gray ' ...Microsoft.PowerShell.PlatyPS imported successfully.' } #CreateHelpStart @@ -899,9 +906,8 @@ Add-BuildTask Build { #Synopsis: Invokes all Pester Integration Tests in the Tests\Integration folder (if it exists) Add-BuildTask IntegrationTest { if (Test-Path -Path $script:IntegrationTestsPath) { - Write-Build White " Importing desired Pester version. Min: $script:MinPesterVersion Max: $script:MaxPesterVersion" - Remove-Module -Name Pester -Force -ErrorAction SilentlyContinue # There are instances where some containers have Pester already in the session - Import-Module -Name Pester -MinimumVersion $script:MinPesterVersion -MaximumVersion $script:MaxPesterVersion -ErrorAction 'Stop' + Write-Build White " Importing exact Pester version: $script:RequiredPesterVersion" + $null = Import-DLLPickleBuildTool -Name 'Pester' -RequiredVersion $script:RequiredPesterVersion Write-Build White " Performing Pester Integration Tests in $script:IntegrationTestsPath" diff --git a/build/DLLPickle.Tooling.ps1 b/build/DLLPickle.Tooling.ps1 new file mode 100644 index 00000000..b5b6ff69 --- /dev/null +++ b/build/DLLPickle.Tooling.ps1 @@ -0,0 +1,163 @@ +function Get-DLLPickleBuildToolPolicy { + [CmdletBinding()] + param( + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$Path = (Join-Path -Path $PSScriptRoot -ChildPath 'build-tool-versions.json') + ) + + if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { + throw "Build-tool policy not found: $Path" + } + + $Policy = Get-Content -LiteralPath $Path -Raw -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop + if ($Policy.schemaVersion -ne 1) { + throw "Unsupported build-tool policy schema version '$($Policy.schemaVersion)' in $Path." + } + + $Modules = @($Policy.modules) + if ($Modules.Count -eq 0) { + throw "Build-tool policy contains no modules: $Path" + } + + $Names = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) + foreach ($Module in $Modules) { + if ([string]::IsNullOrWhiteSpace($Module.name)) { + throw "Build-tool policy contains a module with no name: $Path" + } + if (-not $Names.Add([string]$Module.name)) { + throw "Build-tool policy contains duplicate module '$($Module.name)': $Path" + } + + $ParsedVersion = $null + if (-not [version]::TryParse([string]$Module.version, [ref]$ParsedVersion)) { + throw "Build-tool policy contains invalid version '$($Module.version)' for '$($Module.name)': $Path" + } + if ($ParsedVersion.Build -lt 0 -or $ParsedVersion.Revision -ge 0) { + throw "Build-tool policy versions must use Major.Minor.Patch syntax; found '$($Module.version)' for '$($Module.name)'." + } + } + + return $Policy +} + +function Get-DLLPickleBuildToolVersion { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [ValidateNotNull()] + [object]$Policy, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Name + ) + + $MatchingModules = @($Policy.modules | Where-Object { $_.name -eq $Name }) + if ($MatchingModules.Count -ne 1) { + throw "Expected exactly one build-tool policy entry for '$Name'; found $($MatchingModules.Count)." + } + + return [version]$MatchingModules[0].version +} + +function Test-DLLPickleCommandParameter { + [CmdletBinding()] + [OutputType([bool])] + param( + [Parameter(Mandatory)] + [ValidateNotNull()] + [System.Management.Automation.CommandInfo]$Command, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$ParameterName + ) + + return $Command.Parameters.ContainsKey($ParameterName) +} + +function Test-DLLPickleToolVersionMatch { + [CmdletBinding()] + [OutputType([bool])] + param( + [Parameter(Mandatory)] + [version]$ActualVersion, + + [Parameter(Mandatory)] + [version]$RequiredVersion + ) + + if ( + $ActualVersion.Major -ne $RequiredVersion.Major -or + $ActualVersion.Minor -ne $RequiredVersion.Minor -or + $ActualVersion.Build -ne $RequiredVersion.Build + ) { + return $false + } + + return $RequiredVersion.Revision -lt 0 -or $ActualVersion.Revision -eq $RequiredVersion.Revision +} + +function Assert-DLLPicklePesterAssemblyVersion { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [version]$RequiredVersion, + + [Parameter(Mandatory)] + [System.Reflection.AssemblyName]$LoadedAssemblyName + ) + + if (-not (Test-DLLPickleToolVersionMatch -ActualVersion $LoadedAssemblyName.Version -RequiredVersion $RequiredVersion)) { + throw "Pester assembly mismatch: version '$($LoadedAssemblyName.Version)' is already loaded, but version '$RequiredVersion' is required. Start a fresh pwsh -NoProfile -NonInteractive process." + } +} + +function Import-DLLPickleBuildTool { + [CmdletBinding()] + [OutputType([System.Management.Automation.PSModuleInfo])] + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Name, + + [Parameter(Mandatory)] + [version]$RequiredVersion + ) + + if ($Name -eq 'Pester') { + $LoadedPesterAssemblyNames = @( + [System.AppDomain]::CurrentDomain.GetAssemblies() | + Where-Object { $_.GetName().Name -eq 'Pester' } | + ForEach-Object { $_.GetName() } + ) + foreach ($LoadedAssemblyName in $LoadedPesterAssemblyNames) { + Assert-DLLPicklePesterAssemblyVersion -RequiredVersion $RequiredVersion -LoadedAssemblyName $LoadedAssemblyName + } + } + + $LoadedModules = @(Get-Module -Name $Name) + foreach ($LoadedModule in $LoadedModules) { + if (-not (Test-DLLPickleToolVersionMatch -ActualVersion $LoadedModule.Version -RequiredVersion $RequiredVersion)) { + throw "Build-tool module mismatch: '$Name' version '$($LoadedModule.Version)' is already loaded, but version '$RequiredVersion' is required. Start a fresh pwsh -NoProfile -NonInteractive process." + } + } + + $ExactLoadedModule = $LoadedModules | + Where-Object { Test-DLLPickleToolVersionMatch -ActualVersion $_.Version -RequiredVersion $RequiredVersion } | + Select-Object -First 1 + if ($ExactLoadedModule) { + return $ExactLoadedModule + } + + Import-Module -Name $Name -RequiredVersion $RequiredVersion -Global -ErrorAction Stop + $ImportedModule = Get-Module -Name $Name | + Where-Object { Test-DLLPickleToolVersionMatch -ActualVersion $_.Version -RequiredVersion $RequiredVersion } | + Select-Object -First 1 + if (-not $ImportedModule) { + throw "Failed to import exact build-tool module '$Name' version '$RequiredVersion'." + } + + return $ImportedModule +} diff --git a/build/artifact-size-baseline.json b/build/artifact-size-baseline.json new file mode 100644 index 00000000..e1a4b47e --- /dev/null +++ b/build/artifact-size-baseline.json @@ -0,0 +1,32 @@ +{ + "schemaVersion": 1, + "approvalStatus": "accepted", + "capturedAtUtc": "2026-08-09T03:43:28Z", + "approvedAtUtc": "2026-08-09T17:36:05Z", + "thresholds": { + "policy": "Allowed unpacked growth is the larger of maximumIncreasePercent of the baseline or maximumIncreaseBytes; the limits are not cumulative.", + "maximumIncreasePercent": 10, + "maximumIncreaseBytes": 2097152 + }, + "profiles": [ + { + "name": "net8.0", + "unpackedBytes": 65217096, + "compressedBytes": 21966070 + }, + { + "name": "net9.0", + "unpackedBytes": 65217056, + "compressedBytes": 21966426 + }, + { + "name": "net10.0", + "unpackedBytes": 65217176, + "compressedBytes": 21966409 + } + ], + "fullArtifact": { + "unpackedBytes": 195783343, + "compressedBytes": 65935253 + } +} diff --git a/build/authenticated-evidence/README.md b/build/authenticated-evidence/README.md new file mode 100644 index 00000000..98992195 --- /dev/null +++ b/build/authenticated-evidence/README.md @@ -0,0 +1,24 @@ +# Authenticated compatibility transition evidence + +This directory holds the schema and, only after an explicit maintainer review, +the sanitized manual compatibility record for the initial `3.0.0` multi-target +release. + +The temporary bridge is intentionally narrower than the future protected +credentialed workflow: + +- exact bundle-source fingerprint, including packaging logic and pinned build tooling; +- byte-level prepared module-inventory fingerprint per exact profile and checkpoint; +- exact release version `3.0.0`; +- exact Windows x64 PowerShell 7.4, 7.5, and 7.6 profiles; +- fixed read-only probe and import-order identifiers; +- no credential material or raw service output; +- `WritesPerformed: false` at every level; +- exactly 14 days of validity from capture-session start, with no resume renewal; +- explicit maintainer, confidence, and acceptance timestamp; +- explicit acknowledgement that delegated interactive authentication is not + least-privilege workload-identity evidence. + +`Test-DLLPickleManualAuthenticatedEvidence.ps1` is the authoritative semantic +validator. The JSON schema is a review aid, not an authorization mechanism. No +accepted evidence file is committed until the interactive run is complete. diff --git a/build/authenticated-evidence/manual-transition.schema.json b/build/authenticated-evidence/manual-transition.schema.json new file mode 100644 index 00000000..e23617ec --- /dev/null +++ b/build/authenticated-evidence/manual-transition.schema.json @@ -0,0 +1,215 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/SamErde/DLLPickle/build/authenticated-evidence/manual-transition.schema.json", + "title": "DLLPickle manual authenticated compatibility transition evidence", + "type": "object", + "required": [ + "schemaVersion", + "evidenceType", + "contentFingerprint", + "provenance", + "acceptance", + "content" + ], + "properties": { + "schemaVersion": { "const": 1 }, + "evidenceType": { "const": "manual-interactive-transition" }, + "contentFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, + "provenance": { + "type": "object", + "required": ["sourceCommitSha", "captureStartedAtUtc", "captureCompletedAtUtc"], + "properties": { + "sourceCommitSha": { "type": "string", "pattern": "^[a-f0-9]{40}$" }, + "captureStartedAtUtc": { "type": "string", "format": "date-time" }, + "captureCompletedAtUtc": { "type": "string", "format": "date-time" } + }, + "additionalProperties": false + }, + "acceptance": { + "type": "object", + "required": ["status", "acceptedAtUtc", "acceptedBy", "confidence"], + "properties": { + "status": { "enum": ["pending", "accepted"] }, + "acceptedAtUtc": { "type": ["string", "null"], "format": "date-time" }, + "acceptedBy": { "type": ["string", "null"] }, + "confidence": { "enum": [null, "low", "medium", "high"] } + }, + "allOf": [ + { + "if": { + "properties": { "status": { "const": "accepted" } }, + "required": ["status"] + }, + "then": { + "properties": { + "acceptedAtUtc": { "type": "string", "format": "date-time" }, + "acceptedBy": { "type": "string", "minLength": 1 }, + "confidence": { "enum": ["low", "medium", "high"] } + } + } + } + ], + "additionalProperties": false + }, + "content": { + "type": "object", + "required": [ + "bridge", + "bundleSourceFingerprint", + "credentialMode", + "credentialMaterialCaptured", + "authorizationBoundaryValidated", + "platformScope", + "writesPerformed", + "profiles" + ], + "properties": { + "bridge": { + "type": "object", + "required": ["id", "allowedReleaseVersion", "expiresAtUtc"], + "properties": { + "id": { "const": "initial-powershell-7.4-7.6-multitargeting-major" }, + "allowedReleaseVersion": { "const": "3.0.0" }, + "expiresAtUtc": { "type": "string", "format": "date-time" } + }, + "additionalProperties": false + }, + "bundleSourceFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, + "credentialMode": { "const": "delegated-interactive" }, + "credentialMaterialCaptured": { "const": false }, + "authorizationBoundaryValidated": { "const": false }, + "platformScope": { "const": "windows-x64-only" }, + "writesPerformed": { "const": false }, + "profiles": { + "type": "array", + "minItems": 3, + "maxItems": 3, + "items": { "$ref": "#/$defs/profile" } + } + }, + "additionalProperties": false + } + }, + "$defs": { + "moduleVersion": { + "type": "object", + "required": ["name", "version", "manifest"], + "properties": { + "name": { "type": "string", "minLength": 1 }, + "version": { "type": "string", "minLength": 1 }, + "manifest": { "type": "string", "pattern": "^upstream:" } + }, + "additionalProperties": false + }, + "assembly": { + "type": "object", + "required": ["name", "version", "sha256", "selectedAsset", "assemblyLoadContext", "isCollectible"], + "properties": { + "name": { "type": "string", "minLength": 1 }, + "version": { "type": "string", "minLength": 1 }, + "sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, + "selectedAsset": { "type": "string", "pattern": "^(upstream|dllpickle|runtime):" }, + "assemblyLoadContext": { "type": "string", "minLength": 1 }, + "isCollectible": { "type": "boolean" } + }, + "additionalProperties": false + }, + "snapshot": { + "type": "object", + "required": ["stage", "assemblies"], + "properties": { + "stage": { "enum": ["before-authentication", "after-connection", "after-read-probe"] }, + "assemblies": { "type": "array", "items": { "$ref": "#/$defs/assembly" } } + }, + "additionalProperties": false + }, + "probe": { + "type": "object", + "required": ["probeId", "executed", "status", "durationMilliseconds", "writesPerformed", "errorType"], + "properties": { + "probeId": { "type": "string", "minLength": 1 }, + "executed": { "const": true }, + "status": { "const": "passed" }, + "durationMilliseconds": { "type": "integer", "minimum": 0 }, + "writesPerformed": { "const": false }, + "errorType": { "type": "null" } + }, + "additionalProperties": false + }, + "scenario": { + "type": "object", + "required": [ + "scenarioId", + "profileKey", + "powerShellVersion", + "targetFramework", + "platform", + "architecture", + "inventoryFingerprint", + "importOrder", + "dllPickleTiming", + "expectedTokenAudiences", + "status", + "writesPerformed", + "probes", + "snapshots", + "errorType" + ], + "properties": { + "scenarioId": { "type": "string", "minLength": 1 }, + "profileKey": { "type": "string", "pattern": "^ps7\\.[456]-net(?:8|9|10)\\.0-windows-x64$" }, + "powerShellVersion": { "type": "string", "pattern": "^7\\.[456]\\.[0-9]+$" }, + "targetFramework": { "enum": ["net8.0", "net9.0", "net10.0"] }, + "platform": { "const": "windows" }, + "architecture": { "const": "x64" }, + "inventoryFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, + "importOrder": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 } }, + "dllPickleTiming": { "enum": ["module-only", "dllpickle-first", "module-first"] }, + "expectedTokenAudiences": { "type": "array", "minItems": 1, "items": { "type": "string", "format": "uri" } }, + "status": { "const": "passed" }, + "writesPerformed": { "const": false }, + "probes": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/probe" } }, + "snapshots": { "type": "array", "minItems": 3, "maxItems": 3, "items": { "$ref": "#/$defs/snapshot" } }, + "errorType": { "type": "null" } + }, + "additionalProperties": false + }, + "profile": { + "type": "object", + "required": [ + "profileKey", + "powerShellVersion", + "powerShellLine", + "dotNetVersion", + "dotNetMajor", + "targetFramework", + "platform", + "architecture", + "runtimeExecutable", + "psHome", + "writesPerformed", + "inventoryFingerprint", + "moduleVersions", + "scenarios" + ], + "properties": { + "profileKey": { "type": "string", "pattern": "^ps7\\.[456]-net(?:8|9|10)\\.0-windows-x64$" }, + "powerShellVersion": { "type": "string", "pattern": "^7\\.[456]\\.[0-9]+$" }, + "powerShellLine": { "enum": ["7.4", "7.5", "7.6"] }, + "dotNetVersion": { "type": "string", "minLength": 1 }, + "dotNetMajor": { "enum": [8, 9, 10] }, + "targetFramework": { "enum": ["net8.0", "net9.0", "net10.0"] }, + "platform": { "const": "windows" }, + "architecture": { "const": "x64" }, + "runtimeExecutable": { "type": "string", "pattern": "^runtime:" }, + "psHome": { "type": "string", "pattern": "^runtime:" }, + "writesPerformed": { "const": false }, + "inventoryFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, + "moduleVersions": { "type": "array", "minItems": 6, "maxItems": 6, "items": { "$ref": "#/$defs/moduleVersion" } }, + "scenarios": { "type": "array", "minItems": 14, "maxItems": 14, "items": { "$ref": "#/$defs/scenario" } } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/build/build-tool-versions.json b/build/build-tool-versions.json new file mode 100644 index 00000000..51f9ba2a --- /dev/null +++ b/build/build-tool-versions.json @@ -0,0 +1,25 @@ +{ + "schemaVersion": 1, + "modules": [ + { + "name": "Pester", + "version": "5.7.1", + "skipPublisherCheck": true + }, + { + "name": "InvokeBuild", + "version": "5.14.23", + "skipPublisherCheck": false + }, + { + "name": "PSScriptAnalyzer", + "version": "1.25.0", + "skipPublisherCheck": false + }, + { + "name": "Microsoft.PowerShell.PlatyPS", + "version": "1.0.3", + "skipPublisherCheck": false + } + ] +} diff --git a/build/dependency-policy.json b/build/dependency-policy.json index 06ca4752..f3dc723e 100644 --- a/build/dependency-policy.json +++ b/build/dependency-policy.json @@ -1,36 +1,54 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "description": "DLLPickle upstream dependency compatibility policy.", + "description": "DLLPickle profile-aware upstream dependency compatibility policy. Classifications are evaluated independently by PowerShell line, TFM, platform, module set, and import order.", "monitoredModules": [ { "name": "Microsoft.Graph.Authentication", "repository": "PSGallery", - "purpose": "Graph authentication stack and Azure.Core compatibility source." + "purpose": "Graph authentication stack and Azure.Core compatibility source.", + "umbrellaModule": "Microsoft.Graph", + "deterministicProbeCommand": "Get-Command Connect-MgGraph | Out-Null", + "authenticatedReadOnlyProbeCommand": "Get-MgContext | Out-Null" }, { "name": "ExchangeOnlineManagement", "repository": "PSGallery", - "purpose": "Exchange identity, broker, and OData compatibility source." + "purpose": "Exchange identity, broker, and OData compatibility source.", + "umbrellaModule": "ExchangeOnlineManagement", + "deterministicProbeCommand": "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "authenticatedReadOnlyProbeCommand": "Get-EXOMailbox -ResultSize 1 | Out-Null" }, { "name": "Az.Storage", "repository": "PSGallery", - "purpose": "Az Storage OData compatibility source for Exchange coexistence." + "purpose": "Az Storage OData compatibility source for Exchange coexistence.", + "umbrellaModule": "Az", + "deterministicProbeCommand": "Get-Command Get-AzStorageAccount | Out-Null", + "authenticatedReadOnlyProbeCommand": "Get-AzStorageAccount | Select-Object -First 1 | Out-Null" }, { "name": "Az.Accounts", "repository": "PSGallery", - "purpose": "Az identity stack compatibility source." + "purpose": "Az identity stack compatibility source.", + "umbrellaModule": "Az", + "deterministicProbeCommand": "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "authenticatedReadOnlyProbeCommand": "Get-AzContext | Out-Null" }, { "name": "MicrosoftTeams", "repository": "PSGallery", - "purpose": "Teams identity stack compatibility source." + "purpose": "Teams identity stack compatibility source.", + "umbrellaModule": "MicrosoftTeams", + "deterministicProbeCommand": "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "authenticatedReadOnlyProbeCommand": "Connect-MicrosoftTeams -AccessTokens @($env:DLLPICKLE_GRAPH_ACCESS_TOKEN, $env:DLLPICKLE_TEAMS_ACCESS_TOKEN) | Out-Null; try { Get-CsTenant | Out-Null } finally { Disconnect-MicrosoftTeams | Out-Null }" }, { "name": "Az.Resources", "repository": "PSGallery", - "purpose": "Observed #193 collision source for Microsoft.Extensions.* transitive assemblies." + "purpose": "Observed #193 collision source for Microsoft.Extensions.* transitive assemblies.", + "umbrellaModule": "Az", + "deterministicProbeCommand": "Get-Command Get-AzResource | Out-Null", + "authenticatedReadOnlyProbeCommand": "Get-AzResource | Select-Object -First 1 | Out-Null" } ], "trackedAssemblies": [ @@ -322,7 +340,6 @@ { "packageName": "Microsoft.Identity.Client", "assemblyName": "Microsoft.Identity.Client", - "targetFramework": "net8.0", "classification": "preload", "versionPolicy": "minorPatchFloat", "sourceModules": [ @@ -337,12 +354,16 @@ "alcOwner": "Default", "basis": "In the cross-module conflict surface; default-ALC-shared. Preloading the MSAL line is validated by the 2.0.1/2.0.2 four-module connect and underpins the #156 broker fix.", "decidedOn": "2026-05-31" - } + }, + "targetFrameworks": [ + "net8.0", + "net9.0", + "net10.0" + ] }, { "packageName": "Microsoft.Identity.Client.Broker", "assemblyName": "Microsoft.Identity.Client.Broker", - "targetFramework": "net8.0", "classification": "preload", "versionPolicy": "minorPatchFloat", "sourceModules": [ @@ -357,12 +378,16 @@ "alcOwner": "Default", "basis": "Default-ALC-shared; the #156 reproduction is a WithBroker missing-method failure when the broker line is misaligned. Validated by the 2.0.1/2.0.2 four-module connect.", "decidedOn": "2026-05-31" - } + }, + "targetFrameworks": [ + "net8.0", + "net9.0", + "net10.0" + ] }, { "packageName": "Microsoft.Identity.Client.Extensions.Msal", "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", - "targetFramework": "net8.0", "classification": "preload", "versionPolicy": "minorPatchFloat", "sourceModules": [ @@ -376,12 +401,16 @@ "alcOwner": "Default", "basis": "Block CANDIDATE by ALC ownership (observed in Az's AzSharedAssemblyLoadContext), but runtime decides PRELOAD: preloading the MSAL/cache-helper line is proven safe by the 2.0.1/2.0.2 four-module connect. Only the Azure SDK stack breaks when preloaded.", "decidedOn": "2026-05-31" - } + }, + "targetFrameworks": [ + "net8.0", + "net9.0", + "net10.0" + ] }, { "packageName": "Microsoft.Identity.Client.NativeInterop", "assemblyName": "Microsoft.Identity.Client.NativeInterop", - "targetFramework": "net8.0", "classification": "preload", "versionPolicy": "minorPatchFloat", "sourceModules": [ @@ -394,12 +423,16 @@ "alcOwner": "Default", "basis": "Ships native runtime DLLs that Import-DPLibrary surfaces on PATH. Tracks the broker requirement; validated by the 2.0.1/2.0.2 four-module connect.", "decidedOn": "2026-05-31" - } + }, + "targetFrameworks": [ + "net8.0", + "net9.0", + "net10.0" + ] }, { "packageName": "Microsoft.IdentityModel.Abstractions", "assemblyName": "Microsoft.IdentityModel.Abstractions", - "targetFramework": "net8.0", "classification": "preload", "versionPolicy": "minorPatchFloat", "sourceModules": [ @@ -414,12 +447,16 @@ "alcOwner": "Default", "basis": "Default-ALC-shared token-handling stack in the conflict surface; preloading a coherent 8.x line keeps mixed imports consistent (validated 2.0.1/2.0.2).", "decidedOn": "2026-05-31" - } + }, + "targetFrameworks": [ + "net8.0", + "net9.0", + "net10.0" + ] }, { "packageName": "Microsoft.IdentityModel.JsonWebTokens", "assemblyName": "Microsoft.IdentityModel.JsonWebTokens", - "targetFramework": "net8.0", "classification": "preload", "versionPolicy": "minorPatchFloat", "sourceModules": [ @@ -434,12 +471,16 @@ "alcOwner": "Default", "basis": "Default-ALC-shared token-handling stack in the conflict surface; coherent 8.x preload validated 2.0.1/2.0.2.", "decidedOn": "2026-05-31" - } + }, + "targetFrameworks": [ + "net8.0", + "net9.0", + "net10.0" + ] }, { "packageName": "Microsoft.IdentityModel.Logging", "assemblyName": "Microsoft.IdentityModel.Logging", - "targetFramework": "net8.0", "classification": "preload", "versionPolicy": "minorPatchFloat", "sourceModules": [ @@ -454,12 +495,16 @@ "alcOwner": "Default", "basis": "Default-ALC-shared token-handling stack in the conflict surface; coherent 8.x preload validated 2.0.1/2.0.2.", "decidedOn": "2026-05-31" - } + }, + "targetFrameworks": [ + "net8.0", + "net9.0", + "net10.0" + ] }, { "packageName": "Microsoft.IdentityModel.Tokens", "assemblyName": "Microsoft.IdentityModel.Tokens", - "targetFramework": "net8.0", "classification": "preload", "versionPolicy": "minorPatchFloat", "sourceModules": [ @@ -474,12 +519,16 @@ "alcOwner": "Default", "basis": "Default-ALC-shared token-handling stack in the conflict surface; coherent 8.x preload validated 2.0.1/2.0.2. Its incidental Microsoft.Extensions.* transitives are excluded from the bundle (see blockedPreloadAssemblies / #193).", "decidedOn": "2026-05-31" - } + }, + "targetFrameworks": [ + "net8.0", + "net9.0", + "net10.0" + ] }, { "packageName": "System.IdentityModel.Tokens.Jwt", "assemblyName": "System.IdentityModel.Tokens.Jwt", - "targetFramework": "net8.0", "classification": "preload", "versionPolicy": "minorPatchFloat", "sourceModules": [ @@ -494,7 +543,12 @@ "alcOwner": "Default", "basis": "Default-ALC-shared; aligned with the IdentityModel 8.x preload. Validated 2.0.1/2.0.2.", "decidedOn": "2026-05-31" - } + }, + "targetFrameworks": [ + "net8.0", + "net9.0", + "net10.0" + ] } ], "blockedPreloadAssemblies": [ @@ -508,12 +562,17 @@ "MicrosoftTeams" ], "updateMode": "reportOnly", - "reason": "Azure.Core is intentionally not preloaded on the PowerShell 7.4+ (net8.0) profile. Az.Accounts 5.x isolates its Azure SDK stack in a private AssemblyLoadContext; preloading Azure.Core into the default load context splits Azure.Core.TokenRequestContext across load contexts and breaks Connect-AzAccount with a MissingMethodException on InteractiveBrowserCredential.AuthenticateAsync. Graph, Exchange, and Teams resolve a compatible Azure.Core themselves on .NET 8, so the preload is unnecessary for them. The original net48-only Azure.Core preload (#183) does not apply to the net8.0 baseline.", + "reason": "Azure.Core is intentionally not preloaded on any supported modern runtime profile (net8.0, net9.0, or net10.0). Az.Accounts 5.x isolates its Azure SDK stack in a private AssemblyLoadContext; preloading Azure.Core into the default load context splits Azure.Core.TokenRequestContext across load contexts and breaks Connect-AzAccount with a MissingMethodException on InteractiveBrowserCredential.AuthenticateAsync. Graph, Exchange, and Teams resolve a compatible Azure.Core themselves, so the preload is unnecessary for them. The original net48-only Azure.Core preload (#183) does not apply to the modern profiles.", "evidence": { "alcOwner": "AzSharedAssemblyLoadContext / msgraph-load-context", "basis": "Runtime probe 2026-05-31: Az.Accounts and Microsoft.Graph.Authentication both self-isolate Azure.Core in private ALCs and run different versions side-by-side (1.50 vs 1.51.1). Preloading into the default ALC splits TokenRequestContext -> Connect-AzAccount MissingMethodException. Removed from the bundle in 2.0.1.", "decidedOn": "2026-05-31" - } + }, + "targetFrameworks": [ + "net8.0", + "net9.0", + "net10.0" + ] }, { "packageName": "Azure.Identity", @@ -523,12 +582,17 @@ "Az.Accounts" ], "updateMode": "reportOnly", - "reason": "Azure SDK stack self-isolated by Az.Accounts (and Graph); not preloaded on the net8.0 profile.", + "reason": "Azure SDK stack self-isolated by Az.Accounts (and Graph); not preloaded on the supported net8.0, net9.0, or net10.0 profiles.", "evidence": { "alcOwner": "AzSharedAssemblyLoadContext", "basis": "Runtime probe 2026-05-31: loaded into Az's private ALC (1.13.0). Belongs to the Azure SDK stack that breaks when preloaded (Azure.Core class). Not bundled by DLLPickle.", "decidedOn": "2026-05-31" - } + }, + "targetFrameworks": [ + "net8.0", + "net9.0", + "net10.0" + ] }, { "packageName": "Azure.Identity.Broker", @@ -538,12 +602,17 @@ "Az.Accounts" ], "updateMode": "reportOnly", - "reason": "Azure SDK broker stack self-managed by Az; not preloaded on the net8.0 profile.", + "reason": "Azure SDK broker stack self-managed by Az; not preloaded on the supported net8.0, net9.0, or net10.0 profiles.", "evidence": { "alcOwner": "AzSharedAssemblyLoadContext", "basis": "In the conflict surface; part of the Az-self-isolated Azure SDK stack. Not bundled by DLLPickle.", "decidedOn": "2026-05-31" - } + }, + "targetFrameworks": [ + "net8.0", + "net9.0", + "net10.0" + ] }, { "packageName": "System.ClientModel", @@ -553,12 +622,17 @@ "Microsoft.Graph.Authentication" ], "updateMode": "reportOnly", - "reason": "Azure SDK transitive (Azure.Core dependency) self-managed by Graph/Az; not preloaded on the net8.0 profile.", + "reason": "Azure SDK transitive (Azure.Core dependency) self-managed by Graph/Az; not preloaded on the supported net8.0, net9.0, or net10.0 profiles.", "evidence": { "alcOwner": "msgraph-load-context", "basis": "Runtime probe 2026-05-31: loaded into Graph's private ALC (1.9.0) alongside Azure.Core. Transitive of the blocked Azure.Core. Not bundled by DLLPickle.", "decidedOn": "2026-05-31" - } + }, + "targetFrameworks": [ + "net8.0", + "net9.0", + "net10.0" + ] }, { "packageName": "Microsoft.Extensions.DependencyInjection.Abstractions", @@ -576,7 +650,12 @@ "decidedOn": "2026-05-31", "issue": "193", "trackingScope": "Az.Resources is included in monitoredModules so upstream inventory captures this assembly family from the observed #193 collision source. #193 remains a DLLPickle-bundle-vs-consumer collision (not a cross-module version split), so the cross-module conflict-surface gate still may not flag every change by itself; preserve the regression guard against re-bundling in tests/Integration/DLLPickle.IntegrationTest.Tests.ps1 and re-adjudicate runtime behavior when Az.Resources dependency content changes." - } + }, + "targetFrameworks": [ + "net8.0", + "net9.0", + "net10.0" + ] }, { "packageName": "Microsoft.Extensions.Logging.Abstractions", @@ -593,7 +672,12 @@ "decidedOn": "2026-05-31", "issue": "193", "trackingScope": "Az.Resources does not ship this assembly; the refreshed inventory observes Microsoft.Extensions.Logging.Abstractions only in MicrosoftTeams (a single shipper, so it does not diverge cross-module and is not in the conflict surface). It is excluded from the bundle as the paired dependency of Microsoft.Extensions.DependencyInjection.Abstractions, which Az.Resources does ship and which is now monitored and present in the conflict surface. #193 remains a DLLPickle-bundle-vs-consumer collision; preserve the regression guard against re-bundling in tests/Integration/DLLPickle.IntegrationTest.Tests.ps1 and re-adjudicate runtime behavior when Az.Resources dependency content changes." - } + }, + "targetFrameworks": [ + "net8.0", + "net9.0", + "net10.0" + ] }, { "packageName": "System.Security.Cryptography.ProtectedData", @@ -602,16 +686,22 @@ "platforms": [ "Windows" ], + "universalArtifactRequired": true, "sourceModules": [ "PowerShell" ], "updateMode": "reportOnly", - "reason": "Runtime-provided BCL assembly supplied by PowerShell on Windows; does not require bundling. On non-Windows platforms, PowerShell does not provide this assembly, so it is allowed to be bundled transitively via Microsoft.Identity.Client.Extensions.Msal without creating a conflict.", + "reason": "Runtime-provided BCL assembly supplied by PowerShell on Windows, so Import-DPLibrary must not preload the bundled copy there. PowerShell does not provide it on Linux or macOS, so the single platform-neutral release artifact must include it for the MSAL extensions dependency chain.", "evidence": { "alcOwner": "PowerShell host / Default (Windows)", - "basis": "Windows build validation on 2026-06-23 found that the MSAL extensions dependency copied NuGet System.Security.Cryptography.ProtectedData 4.5.0 into the bundle while PowerShell already supplied the runtime assembly. A direct ExcludeAssets=runtime reference keeps that host-provided assembly out of DLLPickle's preload set. On non-Windows platforms, the runtime assets are retained to satisfy the dependency chain.", + "basis": "Windows validation on 2026-06-23 established that PowerShell supplies this assembly. The release pipeline publishes one module artifact assembled on Windows, so excluding the runtime asset at build time also removed it for Linux and macOS. The universal artifact now retains the file, while the shipped runtime profile policy filters it from Windows preloading and permits it on non-Windows hosts.", "decidedOn": "2026-06-23" - } + }, + "targetFrameworks": [ + "net8.0", + "net9.0", + "net10.0" + ] }, { "packageName": "Microsoft.OData.Core", @@ -625,7 +715,12 @@ "reason": "Issue #174 showed that default OData preloading can break Az.Storage when ExchangeOnlineManagement and Az.Storage require incompatible OData identities.", "evidence": { "basis": "Az.Storage 9.6.1 ships Microsoft.OData.Core 7.6.4; ExchangeOnlineManagement 3.9.2 requires 7.22.0. Both load into the default ALC; no preload version can satisfy both. See issue #174 and the shipped conflict-data entry '174-odata-azstorage-exo' (src/DLLPickle/KnownConflicts.json) for the runtime adjudication." - } + }, + "targetFrameworks": [ + "net8.0", + "net9.0", + "net10.0" + ] }, { "packageName": "Microsoft.OData.Edm", @@ -639,7 +734,12 @@ "reason": "Keep the OData family out of the default preload set unless a future isolation strategy is implemented.", "evidence": { "basis": "OData family dependency paired with Microsoft.OData.Core; same default-ALC conflict as Core. See issue #174 and the shipped conflict-data entry '174-odata-azstorage-exo' (src/DLLPickle/KnownConflicts.json) for the runtime adjudication." - } + }, + "targetFrameworks": [ + "net8.0", + "net9.0", + "net10.0" + ] }, { "packageName": "Microsoft.Spatial", @@ -653,6 +753,302 @@ "reason": "Keep the OData family out of the default preload set unless a future isolation strategy is implemented.", "evidence": { "basis": "OData family dependency paired with Microsoft.OData.Core; same default-ALC conflict as Core. See issue #174 and the shipped conflict-data entry '174-odata-azstorage-exo' (src/DLLPickle/KnownConflicts.json) for the runtime adjudication." + }, + "targetFrameworks": [ + "net8.0", + "net9.0", + "net10.0" + ] + } + ], + "runtimeProfiles": [ + { + "powerShellLine": "7.4", + "targetFramework": "net8.0", + "dotnetMajor": 8, + "baselineStatus": "stale-requires-refresh-issue-273", + "baselineEvidence": "Legacy Windows snapshot from 2026-06-25; not authoritative for other operating systems.", + "platforms": [ + "windows", + "linux", + "macos" + ], + "monitoredModuleSet": [ + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "Az.Storage", + "Az.Accounts", + "MicrosoftTeams", + "Az.Resources" + ], + "importOrders": [ + [ + "ExchangeOnlineManagement", + "MicrosoftTeams", + "Microsoft.Graph.Authentication", + "Az.Accounts" + ], + [ + "Az.Accounts", + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "MicrosoftTeams" + ] + ], + "preloadAssemblyNames": [ + "Microsoft.Identity.Client", + "Microsoft.Identity.Client.Broker", + "Microsoft.Identity.Client.Extensions.Msal", + "Microsoft.Identity.Client.NativeInterop", + "Microsoft.IdentityModel.Abstractions", + "Microsoft.IdentityModel.JsonWebTokens", + "Microsoft.IdentityModel.Logging", + "Microsoft.IdentityModel.Tokens", + "System.IdentityModel.Tokens.Jwt" + ], + "blockedAssemblyNames": [ + "Azure.Core", + "Azure.Identity", + "Azure.Identity.Broker", + "System.ClientModel", + "Microsoft.Extensions.DependencyInjection.Abstractions", + "Microsoft.Extensions.Logging.Abstractions", + "System.Security.Cryptography.ProtectedData", + "Microsoft.OData.Core", + "Microsoft.OData.Edm", + "Microsoft.Spatial" + ], + "knownConflictIds": [ + "174-odata-azstorage-exo" + ], + "validationTiers": { + "deterministicImportNoAuth": { + "required": true, + "credentialsRequired": false + }, + "authenticatedReadOnly": { + "requiredBeforeRelease": true, + "credentialsRequired": true, + "writesAllowed": false, + "status": "not-run-no-approved-credentials" + } + }, + "baselines": { + "windows": { + "status": "accepted", + "conflictSurfaceFingerprint": "b2ef2e3b81097f9759e215a994053e084fe074faf799b8293b1c24f8ab0a192d", + "scenarioFingerprint": "dc2b138e15169b85da6579fe7e2ac17f81b3853889b6cee45d5c26f8add70d78", + "evidencePath": "profile-evidence/ps7.4-net8.0-windows-x64.json", + "evidenceFingerprint": "db3ea852a32ee2f9dcd21d44efc272ff18fd80157a4a036417bd2b2c73d6ae9e" + }, + "linux": { + "status": "accepted", + "conflictSurfaceFingerprint": "f2ed8b0b9b1c45a252ca86d06f3f7c4a1a1200f95a7e94e9446cca421f53b000", + "scenarioFingerprint": "4cbde0f6edd69e935b7a2948bbf3c36293ba5abe2333007b11092b088c55ef19", + "evidencePath": "profile-evidence/ps7.4-net8.0-linux-x64.json", + "evidenceFingerprint": "3e5a3e1d771b9cc6ee31f1605a037a2af26493ed6858bb528a54b597de575283" + }, + "macos": { + "status": "accepted", + "conflictSurfaceFingerprint": "0aadbc0d363b8a16bb6b591c6a1f7d61266a6bb60aa02f23dac8eb9c9b1547a8", + "scenarioFingerprint": "95992a216513d3223431587940d36bd89758af9562e201c7ad40989fbeb220bc", + "evidencePath": "profile-evidence/ps7.4-net8.0-macos-x64.json", + "evidenceFingerprint": "a1ffa4ad38d1aa25421931e86c9617e0477ace74dc7cbcb7fac882ff62eb19da" + } + }, + "legacyUnscopedBaseline": { + "conflictSurfaceFingerprint": "03b94d1603f2d7af09021a738fe9b7ed028dd18bba361e55bbe866bb1febaae0", + "evidence": "Legacy Windows snapshot from 2026-06-25; not authoritative for other operating systems." + } + }, + { + "powerShellLine": "7.5", + "targetFramework": "net9.0", + "dotnetMajor": 9, + "baselineStatus": "requires-ci-evidence", + "baselineEvidence": "No profile-specific baseline has been accepted; the nine-cell CI evidence lane must run.", + "platforms": [ + "windows", + "linux", + "macos" + ], + "monitoredModuleSet": [ + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "Az.Storage", + "Az.Accounts", + "MicrosoftTeams", + "Az.Resources" + ], + "importOrders": [ + [ + "ExchangeOnlineManagement", + "MicrosoftTeams", + "Microsoft.Graph.Authentication", + "Az.Accounts" + ], + [ + "Az.Accounts", + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "MicrosoftTeams" + ] + ], + "preloadAssemblyNames": [ + "Microsoft.Identity.Client", + "Microsoft.Identity.Client.Broker", + "Microsoft.Identity.Client.Extensions.Msal", + "Microsoft.Identity.Client.NativeInterop", + "Microsoft.IdentityModel.Abstractions", + "Microsoft.IdentityModel.JsonWebTokens", + "Microsoft.IdentityModel.Logging", + "Microsoft.IdentityModel.Tokens", + "System.IdentityModel.Tokens.Jwt" + ], + "blockedAssemblyNames": [ + "Azure.Core", + "Azure.Identity", + "Azure.Identity.Broker", + "System.ClientModel", + "Microsoft.Extensions.DependencyInjection.Abstractions", + "Microsoft.Extensions.Logging.Abstractions", + "System.Security.Cryptography.ProtectedData", + "Microsoft.OData.Core", + "Microsoft.OData.Edm", + "Microsoft.Spatial" + ], + "knownConflictIds": [ + "174-odata-azstorage-exo" + ], + "validationTiers": { + "deterministicImportNoAuth": { + "required": true, + "credentialsRequired": false + }, + "authenticatedReadOnly": { + "requiredBeforeRelease": true, + "credentialsRequired": true, + "writesAllowed": false, + "status": "not-run-no-approved-credentials" + } + }, + "baselines": { + "windows": { + "status": "accepted", + "conflictSurfaceFingerprint": "335a11b2b849902e213614891c43dcf864f40d193ee587e10dc58a6ccd40d729", + "scenarioFingerprint": "36684a8b67017021ed7540ccc1c4eaaf2d4a53a1a4c807f4433f78c7d324d001", + "evidencePath": "profile-evidence/ps7.5-net9.0-windows-x64.json", + "evidenceFingerprint": "ac62ce357b779bddc117d6234d1fbf3edcbc09bfa755053f1ce32d97dbe8dc4f" + }, + "linux": { + "status": "accepted", + "conflictSurfaceFingerprint": "16eaccc3fd30948d4ff09ef9228345229de68e53e4fe0f100d9683c513a1fc84", + "scenarioFingerprint": "af15d53b9bc05c4423165de6d8c13b3e3445d3fb0cdab010e7cf54982ab78980", + "evidencePath": "profile-evidence/ps7.5-net9.0-linux-x64.json", + "evidenceFingerprint": "00f9cdd3507b3480950cb9cf2230e86405e98ef88eca08e05bc62bf4f6fa34d3" + }, + "macos": { + "status": "accepted", + "conflictSurfaceFingerprint": "1cc79f2f3af66f0d4b1793e5311df21066980435e89c75ba710a2f643b9884d7", + "scenarioFingerprint": "4e39d071eda5c4fa9be584bb1805a0932c0fc8113e1f27194aa6760d3a3f541a", + "evidencePath": "profile-evidence/ps7.5-net9.0-macos-x64.json", + "evidenceFingerprint": "dd70b37eec9f32c08c75198544dbc9f40635ea3046287d835c9d0c5074a6831d" + } + } + }, + { + "powerShellLine": "7.6", + "targetFramework": "net10.0", + "dotnetMajor": 10, + "baselineStatus": "requires-ci-evidence", + "baselineEvidence": "No profile-specific baseline has been accepted; the nine-cell CI evidence lane must run.", + "platforms": [ + "windows", + "linux", + "macos" + ], + "monitoredModuleSet": [ + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "Az.Storage", + "Az.Accounts", + "MicrosoftTeams", + "Az.Resources" + ], + "importOrders": [ + [ + "ExchangeOnlineManagement", + "MicrosoftTeams", + "Microsoft.Graph.Authentication", + "Az.Accounts" + ], + [ + "Az.Accounts", + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "MicrosoftTeams" + ] + ], + "preloadAssemblyNames": [ + "Microsoft.Identity.Client", + "Microsoft.Identity.Client.Broker", + "Microsoft.Identity.Client.Extensions.Msal", + "Microsoft.Identity.Client.NativeInterop", + "Microsoft.IdentityModel.Abstractions", + "Microsoft.IdentityModel.JsonWebTokens", + "Microsoft.IdentityModel.Logging", + "Microsoft.IdentityModel.Tokens", + "System.IdentityModel.Tokens.Jwt" + ], + "blockedAssemblyNames": [ + "Azure.Core", + "Azure.Identity", + "Azure.Identity.Broker", + "System.ClientModel", + "Microsoft.Extensions.DependencyInjection.Abstractions", + "Microsoft.Extensions.Logging.Abstractions", + "System.Security.Cryptography.ProtectedData", + "Microsoft.OData.Core", + "Microsoft.OData.Edm", + "Microsoft.Spatial" + ], + "knownConflictIds": [ + "174-odata-azstorage-exo" + ], + "validationTiers": { + "deterministicImportNoAuth": { + "required": true, + "credentialsRequired": false + }, + "authenticatedReadOnly": { + "requiredBeforeRelease": true, + "credentialsRequired": true, + "writesAllowed": false, + "status": "not-run-no-approved-credentials" + } + }, + "baselines": { + "windows": { + "status": "accepted", + "conflictSurfaceFingerprint": "1c5b37d886f13577fa1380c11ede9dc225fe517a281103b8076a8924d90389f3", + "scenarioFingerprint": "ff71ef233c126de350e6d01de17838c3963ebc16dcbad5b1598a03ffd12b57b7", + "evidencePath": "profile-evidence/ps7.6-net10.0-windows-x64.json", + "evidenceFingerprint": "a9f7107ef9360feef67f1eca1fb7afcabdb217f1c14b6415bdcbba76df07a48e" + }, + "linux": { + "status": "accepted", + "conflictSurfaceFingerprint": "96c2933d465d2c94b3f7caf53095788bed1ea51dd4a46290a88ec5a4a60ca643", + "scenarioFingerprint": "9ce72aabd07190fbe7f42070b45756b2d9297256357e0ae0798978ae14265f73", + "evidencePath": "profile-evidence/ps7.6-net10.0-linux-x64.json", + "evidenceFingerprint": "8f7e16648edefdfdf0f4f7edb09984c5fb43788363a770f8f36ce594a40938c8" + }, + "macos": { + "status": "accepted", + "conflictSurfaceFingerprint": "e7f221fbab3f96e5cf10fb6c21d811e56d1b58b0aa079c8c4b1869573442ba56", + "scenarioFingerprint": "9c23ccd74eed68a1ef25d4d9da3b39ed797d445b1ba1ffb4db5b6d65469e55e6", + "evidencePath": "profile-evidence/ps7.6-net10.0-macos-x64.json", + "evidenceFingerprint": "631d8a315d5b16a3b417e2089cb6daf0629125986b3eec82335e8f4b283b8ea7" + } } } ] diff --git a/build/powershell-test-matrix.json b/build/powershell-test-matrix.json new file mode 100644 index 00000000..a4c2536f --- /dev/null +++ b/build/powershell-test-matrix.json @@ -0,0 +1,170 @@ +{ + "schemaVersion": 1, + "lastVerifiedUtc": "2026-08-09T01:11:47Z", + "lifecycleSourceUrl": "https://learn.microsoft.com/en-us/lifecycle/products/powershell", + "evidenceFreshnessDays": 30, + "retirementWarningDays": 90, + "profiles": [ + { + "powerShellVersion": "7.4.18", + "powerShellMajor": 7, + "powerShellMinor": 4, + "dotnetMajor": 8, + "dotnetRuntimeVersion": "8.0.29", + "targetFramework": "net8.0", + "lifecycleState": "Supported", + "lifecycleEndDate": "2026-11-10", + "releaseNotesUrl": "https://learn.microsoft.com/en-us/powershell/scripting/whats-new/what-s-new-in-powershell-74" + }, + { + "powerShellVersion": "7.5.9", + "powerShellMajor": 7, + "powerShellMinor": 5, + "dotnetMajor": 9, + "dotnetRuntimeVersion": "9.0.18", + "targetFramework": "net9.0", + "lifecycleState": "Supported", + "lifecycleEndDate": "2026-11-10", + "releaseNotesUrl": "https://learn.microsoft.com/en-us/powershell/scripting/whats-new/what-s-new-in-powershell-75" + }, + { + "powerShellVersion": "7.6.4", + "powerShellMajor": 7, + "powerShellMinor": 6, + "dotnetMajor": 10, + "dotnetRuntimeVersion": "10.0.10", + "targetFramework": "net10.0", + "lifecycleState": "Supported", + "lifecycleEndDate": "2028-11-14", + "releaseNotesUrl": "https://learn.microsoft.com/en-us/powershell/scripting/whats-new/what-s-new-in-powershell-76" + } + ], + "lanes": [ + { + "platform": "windows", + "runner": "windows-2025", + "architecture": "x64", + "executableName": "pwsh.exe" + }, + { + "platform": "linux", + "runner": "ubuntu-24.04", + "architecture": "x64", + "executableName": "pwsh" + }, + { + "platform": "macos", + "runner": "macos-15-intel", + "architecture": "x64", + "executableName": "pwsh" + } + ], + "archiveAssets": [ + { + "powerShellVersion": "7.4.18", + "platform": "windows", + "architecture": "x64", + "fileName": "PowerShell-7.4.18-win-x64.zip", + "sha256": "d018ed5f92ff15a28442dce6a804b1e2aa6153d9fe1e9a06de8fd8142b171f4a", + "downloadUrl": "https://github.com/PowerShell/PowerShell/releases/download/v7.4.18/PowerShell-7.4.18-win-x64.zip" + }, + { + "powerShellVersion": "7.4.18", + "platform": "linux", + "architecture": "x64", + "fileName": "powershell-7.4.18-linux-x64.tar.gz", + "sha256": "21962bfc832119fc8a58e5eba24bc48f0d31707ce94a4e48a90178a223eba619", + "downloadUrl": "https://github.com/PowerShell/PowerShell/releases/download/v7.4.18/powershell-7.4.18-linux-x64.tar.gz" + }, + { + "powerShellVersion": "7.4.18", + "platform": "macos", + "architecture": "x64", + "fileName": "powershell-7.4.18-osx-x64.tar.gz", + "sha256": "7bcd1c95f3ae6e859a8209766db075bea6edf31952017e624ec79fb02879ab9d", + "downloadUrl": "https://github.com/PowerShell/PowerShell/releases/download/v7.4.18/powershell-7.4.18-osx-x64.tar.gz" + }, + { + "powerShellVersion": "7.5.9", + "platform": "windows", + "architecture": "x64", + "fileName": "PowerShell-7.5.9-win-x64.zip", + "sha256": "1e769394e2cde496bf6c55797a51a88873091415a853fa3b56255b2329fa3ef8", + "downloadUrl": "https://github.com/PowerShell/PowerShell/releases/download/v7.5.9/PowerShell-7.5.9-win-x64.zip" + }, + { + "powerShellVersion": "7.5.9", + "platform": "linux", + "architecture": "x64", + "fileName": "powershell-7.5.9-linux-x64.tar.gz", + "sha256": "492ff26bb958336bf61e597ce19e07648b4003bd2a08659e02f0e3e0446ebfe0", + "downloadUrl": "https://github.com/PowerShell/PowerShell/releases/download/v7.5.9/powershell-7.5.9-linux-x64.tar.gz" + }, + { + "powerShellVersion": "7.5.9", + "platform": "macos", + "architecture": "x64", + "fileName": "powershell-7.5.9-osx-x64.tar.gz", + "sha256": "528c261a07bc01466559183f868f8ed7fddc7e29c440e2339eba6f8a682a73a1", + "downloadUrl": "https://github.com/PowerShell/PowerShell/releases/download/v7.5.9/powershell-7.5.9-osx-x64.tar.gz" + }, + { + "powerShellVersion": "7.6.4", + "platform": "windows", + "architecture": "x64", + "fileName": "PowerShell-7.6.4-win-x64.zip", + "sha256": "80832551c52809301e6071c8bac977beb5a2f1ec953eb4db9f94deb953333793", + "downloadUrl": "https://github.com/PowerShell/PowerShell/releases/download/v7.6.4/PowerShell-7.6.4-win-x64.zip" + }, + { + "powerShellVersion": "7.6.4", + "platform": "linux", + "architecture": "x64", + "fileName": "powershell-7.6.4-linux-x64.tar.gz", + "sha256": "4471b5a36bfe86ec7af8525d36bb1cacba0128e7aac22d05cc064bc00e604721", + "downloadUrl": "https://github.com/PowerShell/PowerShell/releases/download/v7.6.4/powershell-7.6.4-linux-x64.tar.gz" + }, + { + "powerShellVersion": "7.6.4", + "platform": "macos", + "architecture": "x64", + "fileName": "powershell-7.6.4-osx-x64.tar.gz", + "sha256": "b58e4b96dbdca20c058d4462f33509d386c0d768751344611bc04aaf32e4187c", + "downloadUrl": "https://github.com/PowerShell/PowerShell/releases/download/v7.6.4/powershell-7.6.4-osx-x64.tar.gz" + } + ], + "provisioning": { + "defaultProvider": "DirectArchive", + "optionalProvider": { + "name": "MultiPwsh", + "version": "0.18.0", + "publishedUtc": "2026-08-07T01:40:45Z", + "repositoryUrl": "https://github.com/Devolutions/multi-pwsh", + "releaseUrl": "https://github.com/Devolutions/multi-pwsh/releases/tag/v0.18.0", + "ciOnly": true, + "assets": [ + { + "platform": "windows", + "architecture": "x64", + "fileName": "multi-pwsh-windows-x64.zip", + "sha256": "6998a9fce8ed77307b7802c9763f27762ea5b20dc6c73042f5bf9ac46b2f0ecb", + "downloadUrl": "https://github.com/Devolutions/multi-pwsh/releases/download/v0.18.0/multi-pwsh-windows-x64.zip" + }, + { + "platform": "linux", + "architecture": "x64", + "fileName": "multi-pwsh-linux-x64.zip", + "sha256": "5570ab938790b869782f6b192954f0038de8e4bec57e1ec50d0bd72d69b52a53", + "downloadUrl": "https://github.com/Devolutions/multi-pwsh/releases/download/v0.18.0/multi-pwsh-linux-x64.zip" + }, + { + "platform": "macos", + "architecture": "x64", + "fileName": "multi-pwsh-macos-x64.zip", + "sha256": "c207ddb69404bf42e1bd055f1d35d2480b0d128019b32c156304185f873e1971", + "downloadUrl": "https://github.com/Devolutions/multi-pwsh/releases/download/v0.18.0/multi-pwsh-macos-x64.zip" + } + ] + } + } +} diff --git a/build/profile-evidence/ps7.4-net8.0-linux-x64.json b/build/profile-evidence/ps7.4-net8.0-linux-x64.json new file mode 100644 index 00000000..6435b728 --- /dev/null +++ b/build/profile-evidence/ps7.4-net8.0-linux-x64.json @@ -0,0 +1,1415 @@ +{ + "schemaVersion": 1, + "contentFingerprint": "3e5a3e1d771b9cc6ee31f1605a037a2af26493ed6858bb528a54b597de575283", + "provenance": { + "sourceRunId": "31349870045", + "sourceRunUrl": "https://github.com/SamErde/DLLPickle/actions/runs/31349870045", + "sourceCommitSha": "586b1fed1189d9a34e6b054dbffa07b62a706d97", + "capturedAtUtc": "2026-08-10T02:30:05.0000000+00:00", + "observedOperatingSystems": [ + "Ubuntu 24.04.4 LTS" + ] + }, + "content": { + "profile": { + "profileKey": "ps7.4-net8.0-linux-x64", + "powerShellVersion": "7.4.18", + "powerShellLine": "7.4", + "dotNetVersion": "8.0.29", + "dotNetMajor": 8, + "targetFramework": "net8.0", + "platform": "linux", + "architecture": "x64" + }, + "validation": { + "deterministicImportNoAuth": { + "status": "passed", + "writesPerformed": false, + "conflictSurfaceFingerprint": "f2ed8b0b9b1c45a252ca86d06f3f7c4a1a1200f95a7e94e9446cca421f53b000", + "scenarioFingerprint": "4cbde0f6edd69e935b7a2948bbf3c36293ba5abe2333007b11092b088c55ef19" + }, + "authenticatedReadOnly": { + "status": "not-run-no-approved-credentials", + "writesPerformed": false, + "unexecutedCommands": [ + "Get-MgContext | Out-Null", + "Get-EXOMailbox -ResultSize 1 | Out-Null", + "Get-AzStorageAccount | Select-Object -First 1 | Out-Null", + "Get-AzContext | Out-Null", + "Connect-MicrosoftTeams -AccessTokens @($env:DLLPICKLE_GRAPH_ACCESS_TOKEN, $env:DLLPICKLE_TEAMS_ACCESS_TOKEN) | Out-Null; try { Get-CsTenant | Out-Null } finally { Disconnect-MicrosoftTeams | Out-Null }", + "Get-AzResource | Select-Object -First 1 | Out-Null" + ] + } + }, + "modules": [ + { + "name": "Az.Accounts", + "umbrellaModule": "Az", + "constituentModule": "Az.Accounts", + "version": "5.5.2", + "latestCompatibleVersion": "5.5.2", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + } + ] + }, + { + "name": "Az.Resources", + "umbrellaModule": "Az", + "constituentModule": "Az.Resources", + "version": "10.1.0", + "latestCompatibleVersion": "10.1.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Get-AzResource | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Extensions.DependencyInjection.Abstractions", + "assemblyVersion": "10.0.0.0", + "packageVersionCandidate": "10.0.0", + "fullName": "Microsoft.Extensions.DependencyInjection.Abstractions, Version=10.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "sha256": "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Resources", + "selectedAsset": "upstream:Az.Resources/10.1.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + } + ] + }, + { + "name": "Az.Storage", + "umbrellaModule": "Az", + "constituentModule": "Az.Storage", + "version": "9.7.2", + "latestCompatibleVersion": "9.7.2", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Get-AzStorageAccount | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "assemblyName": "Microsoft.OData.Core", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "assemblyName": "Microsoft.OData.Edm", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "assemblyName": "Microsoft.Spatial", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "assemblyName": "System.ClientModel", + "assemblyVersion": "1.13.0.0", + "packageVersionCandidate": "1.13.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "name": "ExchangeOnlineManagement", + "umbrellaModule": "ExchangeOnlineManagement", + "constituentModule": "ExchangeOnlineManagement", + "version": "3.10.1", + "latestCompatibleVersion": "3.10.1", + "repository": "PSGallery", + "manifestPowerShellVersion": "3.0", + "compatiblePSEditions": [], + "deterministicProbeCommand": "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "selectedAssets": [] + }, + { + "name": "Microsoft.Graph.Authentication", + "umbrellaModule": "Microsoft.Graph", + "constituentModule": "Microsoft.Graph.Authentication", + "version": "2.39.0", + "latestCompatibleVersion": "2.39.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Connect-MgGraph | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.51.1.0", + "packageVersionCandidate": "1.51.1", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "contributor": "Microsoft.Graph.Authentication", + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "assemblyName": "System.ClientModel", + "assemblyVersion": "1.9.0.0", + "packageVersionCandidate": "1.9.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "contributor": "Microsoft.Graph.Authentication", + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + } + ] + }, + { + "name": "MicrosoftTeams", + "umbrellaModule": "MicrosoftTeams", + "constituentModule": "MicrosoftTeams", + "version": "7.9.0", + "latestCompatibleVersion": "7.9.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Microsoft.Identity.Client", + "assemblyVersion": "4.82.0.0", + "packageVersionCandidate": "4.82.0", + "fullName": "Microsoft.Identity.Client, Version=4.82.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "MicrosoftTeams", + "selectedAsset": "upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll" + } + ] + } + ], + "conflictMatrix": { + "conflictSurface": [ + "Azure.Core", + "System.ClientModel" + ], + "assemblies": [ + { + "name": "Azure.Core", + "shippedBy": [ + "Az.Accounts", + "Az.Resources", + "Az.Storage", + "Microsoft.Graph.Authentication" + ], + "versions": [ + "1.51.1.0", + "1.57.0.0" + ], + "hashes": [ + "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext", + "msgraph-load-context" + ], + "diverges": true, + "selections": [ + { + "contributor": "Az.Accounts", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Resources", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Storage", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Microsoft.Graph.Authentication", + "version": "1.51.1.0", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context" + } + ] + }, + { + "name": "Microsoft.Extensions.DependencyInjection.Abstractions", + "shippedBy": [ + "Az.Resources" + ], + "versions": [ + "10.0.0.0" + ], + "hashes": [ + "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Resources", + "version": "10.0.0.0", + "sha256": "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Identity.Client", + "shippedBy": [ + "MicrosoftTeams" + ], + "versions": [ + "4.82.0.0" + ], + "hashes": [ + "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "MicrosoftTeams", + "version": "4.82.0.0", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "shippedBy": [ + "Az.Accounts", + "Az.Resources", + "Az.Storage" + ], + "versions": [ + "4.84.0.0" + ], + "hashes": [ + "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Accounts", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Resources", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Storage", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + } + ] + }, + { + "name": "Microsoft.OData.Core", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.OData.Edm", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Spatial", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "System.ClientModel", + "shippedBy": [ + "Az.Storage", + "Microsoft.Graph.Authentication" + ], + "versions": [ + "1.13.0.0", + "1.9.0.0" + ], + "hashes": [ + "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext", + "msgraph-load-context" + ], + "diverges": true, + "selections": [ + { + "contributor": "Az.Storage", + "version": "1.13.0.0", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Microsoft.Graph.Authentication", + "version": "1.9.0.0", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context" + } + ] + } + ] + }, + "scenarios": [ + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 3, + "importOrder": [ + "Az.Storage", + "ExchangeOnlineManagement" + ], + "importedModuleAssets": [ + "upstream:Az.Storage/9.7.2/Az.Storage.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-Command Get-AzStorageAccount | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 3, + "importOrder": [ + "Az.Storage", + "ExchangeOnlineManagement" + ], + "importedModuleAssets": [ + "upstream:Az.Storage/9.7.2/Az.Storage.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-Command Get-AzStorageAccount | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ae218d71ff0bb6e40052800d3aede328e0c40e076402657386fc81e513f5aacf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0036ab33662802f5446ce2b671f9a40dcfa1aed52b779c92a67d0a575e20b44e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bf9150a9e8b29d448e752e81700791985f7d6a289ef5ba88bbc97190cd6b9f7c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6321eb40aeceb7c6bb8afc2a337fb22f7a2db16f4217a76c41775b881ca717c0", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6b3db59eb6e958c65e856525e0f9644ab96975e0ad066c23440b7c0c541e1706", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "23cc65a4306ea46ad41e6b463fdf9e4ee4df2385d10af082c37c3cb7e300b4ef", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 4, + "importOrder": [ + "ExchangeOnlineManagement", + "Az.Storage" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:Az.Storage/9.7.2/Az.Storage.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Get-AzStorageAccount | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 4, + "importOrder": [ + "ExchangeOnlineManagement", + "Az.Storage" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:Az.Storage/9.7.2/Az.Storage.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Get-AzStorageAccount | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ae218d71ff0bb6e40052800d3aede328e0c40e076402657386fc81e513f5aacf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0036ab33662802f5446ce2b671f9a40dcfa1aed52b779c92a67d0a575e20b44e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bf9150a9e8b29d448e752e81700791985f7d6a289ef5ba88bbc97190cd6b9f7c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6321eb40aeceb7c6bb8afc2a337fb22f7a2db16f4217a76c41775b881ca717c0", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6b3db59eb6e958c65e856525e0f9644ab96975e0ad066c23440b7c0c541e1706", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "23cc65a4306ea46ad41e6b463fdf9e4ee4df2385d10af082c37c3cb7e300b4ef", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-01", + "orderIndex": 1, + "importOrder": [ + "ExchangeOnlineManagement", + "MicrosoftTeams", + "Microsoft.Graph.Authentication", + "Az.Accounts" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.51.1.0", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.82.0.0", + "fullName": "Microsoft.Identity.Client, Version=4.82.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "System.ClientModel", + "version": "1.9.0.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-01", + "orderIndex": 1, + "importOrder": [ + "ExchangeOnlineManagement", + "MicrosoftTeams", + "Microsoft.Graph.Authentication", + "Az.Accounts" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.51.1.0", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ae218d71ff0bb6e40052800d3aede328e0c40e076402657386fc81e513f5aacf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0036ab33662802f5446ce2b671f9a40dcfa1aed52b779c92a67d0a575e20b44e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bf9150a9e8b29d448e752e81700791985f7d6a289ef5ba88bbc97190cd6b9f7c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6321eb40aeceb7c6bb8afc2a337fb22f7a2db16f4217a76c41775b881ca717c0", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6b3db59eb6e958c65e856525e0f9644ab96975e0ad066c23440b7c0c541e1706", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "System.ClientModel", + "version": "1.9.0.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "23cc65a4306ea46ad41e6b463fdf9e4ee4df2385d10af082c37c3cb7e300b4ef", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-02", + "orderIndex": 2, + "importOrder": [ + "Az.Accounts", + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "MicrosoftTeams" + ], + "importedModuleAssets": [ + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "d18da88bce62a54fa71b3cdf9dfa4b4dfc9759521e872721535d5d5ff63a7651", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-02", + "orderIndex": 2, + "importOrder": [ + "Az.Accounts", + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "MicrosoftTeams" + ], + "importedModuleAssets": [ + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ae218d71ff0bb6e40052800d3aede328e0c40e076402657386fc81e513f5aacf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0036ab33662802f5446ce2b671f9a40dcfa1aed52b779c92a67d0a575e20b44e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bf9150a9e8b29d448e752e81700791985f7d6a289ef5ba88bbc97190cd6b9f7c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6321eb40aeceb7c6bb8afc2a337fb22f7a2db16f4217a76c41775b881ca717c0", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6b3db59eb6e958c65e856525e0f9644ab96975e0ad066c23440b7c0c541e1706", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "23cc65a4306ea46ad41e6b463fdf9e4ee4df2385d10af082c37c3cb7e300b4ef", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + } + ] + } +} diff --git a/build/profile-evidence/ps7.4-net8.0-macos-x64.json b/build/profile-evidence/ps7.4-net8.0-macos-x64.json new file mode 100644 index 00000000..c86e1856 --- /dev/null +++ b/build/profile-evidence/ps7.4-net8.0-macos-x64.json @@ -0,0 +1,1415 @@ +{ + "schemaVersion": 1, + "contentFingerprint": "a1ffa4ad38d1aa25421931e86c9617e0477ace74dc7cbcb7fac882ff62eb19da", + "provenance": { + "sourceRunId": "31349870045", + "sourceRunUrl": "https://github.com/SamErde/DLLPickle/actions/runs/31349870045", + "sourceCommitSha": "586b1fed1189d9a34e6b054dbffa07b62a706d97", + "capturedAtUtc": "2026-08-10T02:31:39.0000000+00:00", + "observedOperatingSystems": [ + "macOS 15.7.7" + ] + }, + "content": { + "profile": { + "profileKey": "ps7.4-net8.0-macos-x64", + "powerShellVersion": "7.4.18", + "powerShellLine": "7.4", + "dotNetVersion": "8.0.29", + "dotNetMajor": 8, + "targetFramework": "net8.0", + "platform": "macos", + "architecture": "x64" + }, + "validation": { + "deterministicImportNoAuth": { + "status": "passed", + "writesPerformed": false, + "conflictSurfaceFingerprint": "0aadbc0d363b8a16bb6b591c6a1f7d61266a6bb60aa02f23dac8eb9c9b1547a8", + "scenarioFingerprint": "95992a216513d3223431587940d36bd89758af9562e201c7ad40989fbeb220bc" + }, + "authenticatedReadOnly": { + "status": "not-run-no-approved-credentials", + "writesPerformed": false, + "unexecutedCommands": [ + "Get-MgContext | Out-Null", + "Get-EXOMailbox -ResultSize 1 | Out-Null", + "Get-AzStorageAccount | Select-Object -First 1 | Out-Null", + "Get-AzContext | Out-Null", + "Connect-MicrosoftTeams -AccessTokens @($env:DLLPICKLE_GRAPH_ACCESS_TOKEN, $env:DLLPICKLE_TEAMS_ACCESS_TOKEN) | Out-Null; try { Get-CsTenant | Out-Null } finally { Disconnect-MicrosoftTeams | Out-Null }", + "Get-AzResource | Select-Object -First 1 | Out-Null" + ] + } + }, + "modules": [ + { + "name": "Az.Accounts", + "umbrellaModule": "Az", + "constituentModule": "Az.Accounts", + "version": "5.5.2", + "latestCompatibleVersion": "5.5.2", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + } + ] + }, + { + "name": "Az.Resources", + "umbrellaModule": "Az", + "constituentModule": "Az.Resources", + "version": "10.1.0", + "latestCompatibleVersion": "10.1.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Get-AzResource | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Extensions.DependencyInjection.Abstractions", + "assemblyVersion": "10.0.0.0", + "packageVersionCandidate": "10.0.0", + "fullName": "Microsoft.Extensions.DependencyInjection.Abstractions, Version=10.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "sha256": "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Resources", + "selectedAsset": "upstream:Az.Resources/10.1.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + } + ] + }, + { + "name": "Az.Storage", + "umbrellaModule": "Az", + "constituentModule": "Az.Storage", + "version": "9.7.2", + "latestCompatibleVersion": "9.7.2", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Get-AzStorageAccount | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "assemblyName": "Microsoft.OData.Core", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "assemblyName": "Microsoft.OData.Edm", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "assemblyName": "Microsoft.Spatial", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "assemblyName": "System.ClientModel", + "assemblyVersion": "1.13.0.0", + "packageVersionCandidate": "1.13.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "name": "ExchangeOnlineManagement", + "umbrellaModule": "ExchangeOnlineManagement", + "constituentModule": "ExchangeOnlineManagement", + "version": "3.10.1", + "latestCompatibleVersion": "3.10.1", + "repository": "PSGallery", + "manifestPowerShellVersion": "3.0", + "compatiblePSEditions": [], + "deterministicProbeCommand": "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "selectedAssets": [] + }, + { + "name": "Microsoft.Graph.Authentication", + "umbrellaModule": "Microsoft.Graph", + "constituentModule": "Microsoft.Graph.Authentication", + "version": "2.39.0", + "latestCompatibleVersion": "2.39.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Connect-MgGraph | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.51.1.0", + "packageVersionCandidate": "1.51.1", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "contributor": "Microsoft.Graph.Authentication", + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "assemblyName": "System.ClientModel", + "assemblyVersion": "1.9.0.0", + "packageVersionCandidate": "1.9.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "contributor": "Microsoft.Graph.Authentication", + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + } + ] + }, + { + "name": "MicrosoftTeams", + "umbrellaModule": "MicrosoftTeams", + "constituentModule": "MicrosoftTeams", + "version": "7.9.0", + "latestCompatibleVersion": "7.9.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Microsoft.Identity.Client", + "assemblyVersion": "4.82.0.0", + "packageVersionCandidate": "4.82.0", + "fullName": "Microsoft.Identity.Client, Version=4.82.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "MicrosoftTeams", + "selectedAsset": "upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll" + } + ] + } + ], + "conflictMatrix": { + "conflictSurface": [ + "Azure.Core", + "System.ClientModel" + ], + "assemblies": [ + { + "name": "Azure.Core", + "shippedBy": [ + "Az.Accounts", + "Az.Resources", + "Az.Storage", + "Microsoft.Graph.Authentication" + ], + "versions": [ + "1.51.1.0", + "1.57.0.0" + ], + "hashes": [ + "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext", + "msgraph-load-context" + ], + "diverges": true, + "selections": [ + { + "contributor": "Az.Accounts", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Resources", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Storage", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Microsoft.Graph.Authentication", + "version": "1.51.1.0", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context" + } + ] + }, + { + "name": "Microsoft.Extensions.DependencyInjection.Abstractions", + "shippedBy": [ + "Az.Resources" + ], + "versions": [ + "10.0.0.0" + ], + "hashes": [ + "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Resources", + "version": "10.0.0.0", + "sha256": "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Identity.Client", + "shippedBy": [ + "MicrosoftTeams" + ], + "versions": [ + "4.82.0.0" + ], + "hashes": [ + "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "MicrosoftTeams", + "version": "4.82.0.0", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "shippedBy": [ + "Az.Accounts", + "Az.Resources", + "Az.Storage" + ], + "versions": [ + "4.84.0.0" + ], + "hashes": [ + "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Accounts", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Resources", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Storage", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + } + ] + }, + { + "name": "Microsoft.OData.Core", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.OData.Edm", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Spatial", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "System.ClientModel", + "shippedBy": [ + "Az.Storage", + "Microsoft.Graph.Authentication" + ], + "versions": [ + "1.13.0.0", + "1.9.0.0" + ], + "hashes": [ + "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext", + "msgraph-load-context" + ], + "diverges": true, + "selections": [ + { + "contributor": "Az.Storage", + "version": "1.13.0.0", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Microsoft.Graph.Authentication", + "version": "1.9.0.0", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context" + } + ] + } + ] + }, + "scenarios": [ + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 3, + "importOrder": [ + "Az.Storage", + "ExchangeOnlineManagement" + ], + "importedModuleAssets": [ + "upstream:Az.Storage/9.7.2/Az.Storage.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-Command Get-AzStorageAccount | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 3, + "importOrder": [ + "Az.Storage", + "ExchangeOnlineManagement" + ], + "importedModuleAssets": [ + "upstream:Az.Storage/9.7.2/Az.Storage.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-Command Get-AzStorageAccount | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ae218d71ff0bb6e40052800d3aede328e0c40e076402657386fc81e513f5aacf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0036ab33662802f5446ce2b671f9a40dcfa1aed52b779c92a67d0a575e20b44e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bf9150a9e8b29d448e752e81700791985f7d6a289ef5ba88bbc97190cd6b9f7c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6321eb40aeceb7c6bb8afc2a337fb22f7a2db16f4217a76c41775b881ca717c0", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6b3db59eb6e958c65e856525e0f9644ab96975e0ad066c23440b7c0c541e1706", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "23cc65a4306ea46ad41e6b463fdf9e4ee4df2385d10af082c37c3cb7e300b4ef", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 4, + "importOrder": [ + "ExchangeOnlineManagement", + "Az.Storage" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:Az.Storage/9.7.2/Az.Storage.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Get-AzStorageAccount | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 4, + "importOrder": [ + "ExchangeOnlineManagement", + "Az.Storage" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:Az.Storage/9.7.2/Az.Storage.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Get-AzStorageAccount | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ae218d71ff0bb6e40052800d3aede328e0c40e076402657386fc81e513f5aacf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0036ab33662802f5446ce2b671f9a40dcfa1aed52b779c92a67d0a575e20b44e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bf9150a9e8b29d448e752e81700791985f7d6a289ef5ba88bbc97190cd6b9f7c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6321eb40aeceb7c6bb8afc2a337fb22f7a2db16f4217a76c41775b881ca717c0", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6b3db59eb6e958c65e856525e0f9644ab96975e0ad066c23440b7c0c541e1706", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "23cc65a4306ea46ad41e6b463fdf9e4ee4df2385d10af082c37c3cb7e300b4ef", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-01", + "orderIndex": 1, + "importOrder": [ + "ExchangeOnlineManagement", + "MicrosoftTeams", + "Microsoft.Graph.Authentication", + "Az.Accounts" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.51.1.0", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.82.0.0", + "fullName": "Microsoft.Identity.Client, Version=4.82.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "System.ClientModel", + "version": "1.9.0.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-01", + "orderIndex": 1, + "importOrder": [ + "ExchangeOnlineManagement", + "MicrosoftTeams", + "Microsoft.Graph.Authentication", + "Az.Accounts" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.51.1.0", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ae218d71ff0bb6e40052800d3aede328e0c40e076402657386fc81e513f5aacf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0036ab33662802f5446ce2b671f9a40dcfa1aed52b779c92a67d0a575e20b44e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bf9150a9e8b29d448e752e81700791985f7d6a289ef5ba88bbc97190cd6b9f7c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6321eb40aeceb7c6bb8afc2a337fb22f7a2db16f4217a76c41775b881ca717c0", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6b3db59eb6e958c65e856525e0f9644ab96975e0ad066c23440b7c0c541e1706", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "System.ClientModel", + "version": "1.9.0.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "23cc65a4306ea46ad41e6b463fdf9e4ee4df2385d10af082c37c3cb7e300b4ef", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-02", + "orderIndex": 2, + "importOrder": [ + "Az.Accounts", + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "MicrosoftTeams" + ], + "importedModuleAssets": [ + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "d18da88bce62a54fa71b3cdf9dfa4b4dfc9759521e872721535d5d5ff63a7651", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-02", + "orderIndex": 2, + "importOrder": [ + "Az.Accounts", + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "MicrosoftTeams" + ], + "importedModuleAssets": [ + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ae218d71ff0bb6e40052800d3aede328e0c40e076402657386fc81e513f5aacf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0036ab33662802f5446ce2b671f9a40dcfa1aed52b779c92a67d0a575e20b44e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bf9150a9e8b29d448e752e81700791985f7d6a289ef5ba88bbc97190cd6b9f7c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6321eb40aeceb7c6bb8afc2a337fb22f7a2db16f4217a76c41775b881ca717c0", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6b3db59eb6e958c65e856525e0f9644ab96975e0ad066c23440b7c0c541e1706", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "23cc65a4306ea46ad41e6b463fdf9e4ee4df2385d10af082c37c3cb7e300b4ef", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + } + ] + } +} diff --git a/build/profile-evidence/ps7.4-net8.0-windows-x64.json b/build/profile-evidence/ps7.4-net8.0-windows-x64.json new file mode 100644 index 00000000..6e012321 --- /dev/null +++ b/build/profile-evidence/ps7.4-net8.0-windows-x64.json @@ -0,0 +1,1558 @@ +{ + "schemaVersion": 1, + "contentFingerprint": "db3ea852a32ee2f9dcd21d44efc272ff18fd80157a4a036417bd2b2c73d6ae9e", + "provenance": { + "sourceRunId": "31349870045", + "sourceRunUrl": "https://github.com/SamErde/DLLPickle/actions/runs/31349870045", + "sourceCommitSha": "586b1fed1189d9a34e6b054dbffa07b62a706d97", + "capturedAtUtc": "2026-08-10T02:30:35.0000000+00:00", + "observedOperatingSystems": [ + "Microsoft Windows 10.0.26100" + ] + }, + "content": { + "profile": { + "profileKey": "ps7.4-net8.0-windows-x64", + "powerShellVersion": "7.4.18", + "powerShellLine": "7.4", + "dotNetVersion": "8.0.29", + "dotNetMajor": 8, + "targetFramework": "net8.0", + "platform": "windows", + "architecture": "x64" + }, + "validation": { + "deterministicImportNoAuth": { + "status": "passed", + "writesPerformed": false, + "conflictSurfaceFingerprint": "b2ef2e3b81097f9759e215a994053e084fe074faf799b8293b1c24f8ab0a192d", + "scenarioFingerprint": "dc2b138e15169b85da6579fe7e2ac17f81b3853889b6cee45d5c26f8add70d78" + }, + "authenticatedReadOnly": { + "status": "not-run-no-approved-credentials", + "writesPerformed": false, + "unexecutedCommands": [ + "Get-MgContext | Out-Null", + "Get-EXOMailbox -ResultSize 1 | Out-Null", + "Get-AzStorageAccount | Select-Object -First 1 | Out-Null", + "Get-AzContext | Out-Null", + "Connect-MicrosoftTeams -AccessTokens @($env:DLLPICKLE_GRAPH_ACCESS_TOKEN, $env:DLLPICKLE_TEAMS_ACCESS_TOKEN) | Out-Null; try { Get-CsTenant | Out-Null } finally { Disconnect-MicrosoftTeams | Out-Null }", + "Get-AzResource | Select-Object -First 1 | Out-Null" + ] + } + }, + "modules": [ + { + "name": "Az.Accounts", + "umbrellaModule": "Az", + "constituentModule": "Az.Accounts", + "version": "5.5.2", + "latestCompatibleVersion": "5.5.2", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "assemblyName": "System.Security.Cryptography.ProtectedData", + "assemblyVersion": "8.0.0.0", + "packageVersionCandidate": "8.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "3467af2e0773d2ec3874e5990a7c0efdd0ca0274106b4dc5589ef15a22cb11db", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "name": "Az.Resources", + "umbrellaModule": "Az", + "constituentModule": "Az.Resources", + "version": "10.1.0", + "latestCompatibleVersion": "10.1.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Get-AzResource | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Extensions.DependencyInjection.Abstractions", + "assemblyVersion": "10.0.0.0", + "packageVersionCandidate": "10.0.0", + "fullName": "Microsoft.Extensions.DependencyInjection.Abstractions, Version=10.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "sha256": "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Resources", + "selectedAsset": "upstream:Az.Resources/10.1.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "assemblyName": "System.Security.Cryptography.ProtectedData", + "assemblyVersion": "8.0.0.0", + "packageVersionCandidate": "8.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "3467af2e0773d2ec3874e5990a7c0efdd0ca0274106b4dc5589ef15a22cb11db", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Resources", + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "name": "Az.Storage", + "umbrellaModule": "Az", + "constituentModule": "Az.Storage", + "version": "9.7.2", + "latestCompatibleVersion": "9.7.2", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Get-AzStorageAccount | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "assemblyName": "Microsoft.OData.Core", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "assemblyName": "Microsoft.OData.Edm", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "assemblyName": "Microsoft.Spatial", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "assemblyName": "System.ClientModel", + "assemblyVersion": "1.13.0.0", + "packageVersionCandidate": "1.13.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "assemblyName": "System.Security.Cryptography.ProtectedData", + "assemblyVersion": "8.0.0.0", + "packageVersionCandidate": "8.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "3467af2e0773d2ec3874e5990a7c0efdd0ca0274106b4dc5589ef15a22cb11db", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "name": "ExchangeOnlineManagement", + "umbrellaModule": "ExchangeOnlineManagement", + "constituentModule": "ExchangeOnlineManagement", + "version": "3.10.1", + "latestCompatibleVersion": "3.10.1", + "repository": "PSGallery", + "manifestPowerShellVersion": "3.0", + "compatiblePSEditions": [], + "deterministicProbeCommand": "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "selectedAssets": [] + }, + { + "name": "Microsoft.Graph.Authentication", + "umbrellaModule": "Microsoft.Graph", + "constituentModule": "Microsoft.Graph.Authentication", + "version": "2.39.0", + "latestCompatibleVersion": "2.39.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Connect-MgGraph | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.51.1.0", + "packageVersionCandidate": "1.51.1", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "contributor": "Microsoft.Graph.Authentication", + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "assemblyName": "System.ClientModel", + "assemblyVersion": "1.9.0.0", + "packageVersionCandidate": "1.9.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "contributor": "Microsoft.Graph.Authentication", + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + } + ] + }, + { + "name": "MicrosoftTeams", + "umbrellaModule": "MicrosoftTeams", + "constituentModule": "MicrosoftTeams", + "version": "7.9.0", + "latestCompatibleVersion": "7.9.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Microsoft.Identity.Client", + "assemblyVersion": "4.82.0.0", + "packageVersionCandidate": "4.82.0", + "fullName": "Microsoft.Identity.Client, Version=4.82.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "MicrosoftTeams", + "selectedAsset": "upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll" + } + ] + } + ], + "conflictMatrix": { + "conflictSurface": [ + "Azure.Core", + "System.ClientModel" + ], + "assemblies": [ + { + "name": "Azure.Core", + "shippedBy": [ + "Az.Accounts", + "Az.Resources", + "Az.Storage", + "Microsoft.Graph.Authentication" + ], + "versions": [ + "1.51.1.0", + "1.57.0.0" + ], + "hashes": [ + "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext", + "msgraph-load-context" + ], + "diverges": true, + "selections": [ + { + "contributor": "Az.Accounts", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Resources", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Storage", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Microsoft.Graph.Authentication", + "version": "1.51.1.0", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context" + } + ] + }, + { + "name": "Microsoft.Extensions.DependencyInjection.Abstractions", + "shippedBy": [ + "Az.Resources" + ], + "versions": [ + "10.0.0.0" + ], + "hashes": [ + "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Resources", + "version": "10.0.0.0", + "sha256": "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Identity.Client", + "shippedBy": [ + "MicrosoftTeams" + ], + "versions": [ + "4.82.0.0" + ], + "hashes": [ + "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "MicrosoftTeams", + "version": "4.82.0.0", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "shippedBy": [ + "Az.Accounts", + "Az.Resources", + "Az.Storage" + ], + "versions": [ + "4.84.0.0" + ], + "hashes": [ + "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Accounts", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Resources", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Storage", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + } + ] + }, + { + "name": "Microsoft.OData.Core", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.OData.Edm", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Spatial", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "System.ClientModel", + "shippedBy": [ + "Az.Storage", + "Microsoft.Graph.Authentication" + ], + "versions": [ + "1.13.0.0", + "1.9.0.0" + ], + "hashes": [ + "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext", + "msgraph-load-context" + ], + "diverges": true, + "selections": [ + { + "contributor": "Az.Storage", + "version": "1.13.0.0", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Microsoft.Graph.Authentication", + "version": "1.9.0.0", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context" + } + ] + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "shippedBy": [ + "Az.Accounts", + "Az.Resources", + "Az.Storage" + ], + "versions": [ + "8.0.0.0" + ], + "hashes": [ + "3467af2e0773d2ec3874e5990a7c0efdd0ca0274106b4dc5589ef15a22cb11db" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Accounts", + "version": "8.0.0.0", + "sha256": "3467af2e0773d2ec3874e5990a7c0efdd0ca0274106b4dc5589ef15a22cb11db", + "assemblyLoadContext": "Default" + }, + { + "contributor": "Az.Resources", + "version": "8.0.0.0", + "sha256": "3467af2e0773d2ec3874e5990a7c0efdd0ca0274106b4dc5589ef15a22cb11db", + "assemblyLoadContext": "Default" + }, + { + "contributor": "Az.Storage", + "version": "8.0.0.0", + "sha256": "3467af2e0773d2ec3874e5990a7c0efdd0ca0274106b4dc5589ef15a22cb11db", + "assemblyLoadContext": "Default" + } + ] + } + ] + }, + "scenarios": [ + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 3, + "importOrder": [ + "Az.Storage", + "ExchangeOnlineManagement" + ], + "importedModuleAssets": [ + "upstream:Az.Storage/9.7.2/Az.Storage.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-Command Get-AzStorageAccount | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "8.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "3467af2e0773d2ec3874e5990a7c0efdd0ca0274106b4dc5589ef15a22cb11db", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 3, + "importOrder": [ + "Az.Storage", + "ExchangeOnlineManagement" + ], + "importedModuleAssets": [ + "upstream:Az.Storage/9.7.2/Az.Storage.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-Command Get-AzStorageAccount | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ae218d71ff0bb6e40052800d3aede328e0c40e076402657386fc81e513f5aacf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0036ab33662802f5446ce2b671f9a40dcfa1aed52b779c92a67d0a575e20b44e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bf9150a9e8b29d448e752e81700791985f7d6a289ef5ba88bbc97190cd6b9f7c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6321eb40aeceb7c6bb8afc2a337fb22f7a2db16f4217a76c41775b881ca717c0", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6b3db59eb6e958c65e856525e0f9644ab96975e0ad066c23440b7c0c541e1706", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "23cc65a4306ea46ad41e6b463fdf9e4ee4df2385d10af082c37c3cb7e300b4ef", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/System.IdentityModel.Tokens.Jwt.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "8.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "3467af2e0773d2ec3874e5990a7c0efdd0ca0274106b4dc5589ef15a22cb11db", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 4, + "importOrder": [ + "ExchangeOnlineManagement", + "Az.Storage" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:Az.Storage/9.7.2/Az.Storage.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Get-AzStorageAccount | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "8.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "3467af2e0773d2ec3874e5990a7c0efdd0ca0274106b4dc5589ef15a22cb11db", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 4, + "importOrder": [ + "ExchangeOnlineManagement", + "Az.Storage" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:Az.Storage/9.7.2/Az.Storage.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Get-AzStorageAccount | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ae218d71ff0bb6e40052800d3aede328e0c40e076402657386fc81e513f5aacf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0036ab33662802f5446ce2b671f9a40dcfa1aed52b779c92a67d0a575e20b44e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bf9150a9e8b29d448e752e81700791985f7d6a289ef5ba88bbc97190cd6b9f7c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6321eb40aeceb7c6bb8afc2a337fb22f7a2db16f4217a76c41775b881ca717c0", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6b3db59eb6e958c65e856525e0f9644ab96975e0ad066c23440b7c0c541e1706", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "23cc65a4306ea46ad41e6b463fdf9e4ee4df2385d10af082c37c3cb7e300b4ef", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/System.IdentityModel.Tokens.Jwt.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "8.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "3467af2e0773d2ec3874e5990a7c0efdd0ca0274106b4dc5589ef15a22cb11db", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-01", + "orderIndex": 1, + "importOrder": [ + "ExchangeOnlineManagement", + "MicrosoftTeams", + "Microsoft.Graph.Authentication", + "Az.Accounts" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.51.1.0", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.82.0.0", + "fullName": "Microsoft.Identity.Client, Version=4.82.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "System.ClientModel", + "version": "1.9.0.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "8.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "3467af2e0773d2ec3874e5990a7c0efdd0ca0274106b4dc5589ef15a22cb11db", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-01", + "orderIndex": 1, + "importOrder": [ + "ExchangeOnlineManagement", + "MicrosoftTeams", + "Microsoft.Graph.Authentication", + "Az.Accounts" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.51.1.0", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ae218d71ff0bb6e40052800d3aede328e0c40e076402657386fc81e513f5aacf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0036ab33662802f5446ce2b671f9a40dcfa1aed52b779c92a67d0a575e20b44e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bf9150a9e8b29d448e752e81700791985f7d6a289ef5ba88bbc97190cd6b9f7c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6321eb40aeceb7c6bb8afc2a337fb22f7a2db16f4217a76c41775b881ca717c0", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6b3db59eb6e958c65e856525e0f9644ab96975e0ad066c23440b7c0c541e1706", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "System.ClientModel", + "version": "1.9.0.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "23cc65a4306ea46ad41e6b463fdf9e4ee4df2385d10af082c37c3cb7e300b4ef", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/System.IdentityModel.Tokens.Jwt.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "8.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "3467af2e0773d2ec3874e5990a7c0efdd0ca0274106b4dc5589ef15a22cb11db", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-02", + "orderIndex": 2, + "importOrder": [ + "Az.Accounts", + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "MicrosoftTeams" + ], + "importedModuleAssets": [ + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "d18da88bce62a54fa71b3cdf9dfa4b4dfc9759521e872721535d5d5ff63a7651", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "8.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "3467af2e0773d2ec3874e5990a7c0efdd0ca0274106b4dc5589ef15a22cb11db", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-02", + "orderIndex": 2, + "importOrder": [ + "Az.Accounts", + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "MicrosoftTeams" + ], + "importedModuleAssets": [ + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ae218d71ff0bb6e40052800d3aede328e0c40e076402657386fc81e513f5aacf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0036ab33662802f5446ce2b671f9a40dcfa1aed52b779c92a67d0a575e20b44e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bf9150a9e8b29d448e752e81700791985f7d6a289ef5ba88bbc97190cd6b9f7c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6321eb40aeceb7c6bb8afc2a337fb22f7a2db16f4217a76c41775b881ca717c0", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "6b3db59eb6e958c65e856525e0f9644ab96975e0ad066c23440b7c0c541e1706", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "23cc65a4306ea46ad41e6b463fdf9e4ee4df2385d10af082c37c3cb7e300b4ef", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net8.0/System.IdentityModel.Tokens.Jwt.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "8.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "3467af2e0773d2ec3874e5990a7c0efdd0ca0274106b4dc5589ef15a22cb11db", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + } + ] + } +} diff --git a/build/profile-evidence/ps7.5-net9.0-linux-x64.json b/build/profile-evidence/ps7.5-net9.0-linux-x64.json new file mode 100644 index 00000000..c97a2746 --- /dev/null +++ b/build/profile-evidence/ps7.5-net9.0-linux-x64.json @@ -0,0 +1,1415 @@ +{ + "schemaVersion": 1, + "contentFingerprint": "00f9cdd3507b3480950cb9cf2230e86405e98ef88eca08e05bc62bf4f6fa34d3", + "provenance": { + "sourceRunId": "31349870045", + "sourceRunUrl": "https://github.com/SamErde/DLLPickle/actions/runs/31349870045", + "sourceCommitSha": "586b1fed1189d9a34e6b054dbffa07b62a706d97", + "capturedAtUtc": "2026-08-10T02:29:55.0000000+00:00", + "observedOperatingSystems": [ + "Ubuntu 24.04.4 LTS" + ] + }, + "content": { + "profile": { + "profileKey": "ps7.5-net9.0-linux-x64", + "powerShellVersion": "7.5.9", + "powerShellLine": "7.5", + "dotNetVersion": "9.0.18", + "dotNetMajor": 9, + "targetFramework": "net9.0", + "platform": "linux", + "architecture": "x64" + }, + "validation": { + "deterministicImportNoAuth": { + "status": "passed", + "writesPerformed": false, + "conflictSurfaceFingerprint": "16eaccc3fd30948d4ff09ef9228345229de68e53e4fe0f100d9683c513a1fc84", + "scenarioFingerprint": "af15d53b9bc05c4423165de6d8c13b3e3445d3fb0cdab010e7cf54982ab78980" + }, + "authenticatedReadOnly": { + "status": "not-run-no-approved-credentials", + "writesPerformed": false, + "unexecutedCommands": [ + "Get-MgContext | Out-Null", + "Get-EXOMailbox -ResultSize 1 | Out-Null", + "Get-AzStorageAccount | Select-Object -First 1 | Out-Null", + "Get-AzContext | Out-Null", + "Connect-MicrosoftTeams -AccessTokens @($env:DLLPICKLE_GRAPH_ACCESS_TOKEN, $env:DLLPICKLE_TEAMS_ACCESS_TOKEN) | Out-Null; try { Get-CsTenant | Out-Null } finally { Disconnect-MicrosoftTeams | Out-Null }", + "Get-AzResource | Select-Object -First 1 | Out-Null" + ] + } + }, + "modules": [ + { + "name": "Az.Accounts", + "umbrellaModule": "Az", + "constituentModule": "Az.Accounts", + "version": "5.5.2", + "latestCompatibleVersion": "5.5.2", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + } + ] + }, + { + "name": "Az.Resources", + "umbrellaModule": "Az", + "constituentModule": "Az.Resources", + "version": "10.1.0", + "latestCompatibleVersion": "10.1.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Get-AzResource | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Extensions.DependencyInjection.Abstractions", + "assemblyVersion": "10.0.0.0", + "packageVersionCandidate": "10.0.0", + "fullName": "Microsoft.Extensions.DependencyInjection.Abstractions, Version=10.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "sha256": "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Resources", + "selectedAsset": "upstream:Az.Resources/10.1.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + } + ] + }, + { + "name": "Az.Storage", + "umbrellaModule": "Az", + "constituentModule": "Az.Storage", + "version": "9.7.2", + "latestCompatibleVersion": "9.7.2", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Get-AzStorageAccount | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "assemblyName": "Microsoft.OData.Core", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "assemblyName": "Microsoft.OData.Edm", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "assemblyName": "Microsoft.Spatial", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "assemblyName": "System.ClientModel", + "assemblyVersion": "1.13.0.0", + "packageVersionCandidate": "1.13.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "name": "ExchangeOnlineManagement", + "umbrellaModule": "ExchangeOnlineManagement", + "constituentModule": "ExchangeOnlineManagement", + "version": "3.10.1", + "latestCompatibleVersion": "3.10.1", + "repository": "PSGallery", + "manifestPowerShellVersion": "3.0", + "compatiblePSEditions": [], + "deterministicProbeCommand": "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "selectedAssets": [] + }, + { + "name": "Microsoft.Graph.Authentication", + "umbrellaModule": "Microsoft.Graph", + "constituentModule": "Microsoft.Graph.Authentication", + "version": "2.39.0", + "latestCompatibleVersion": "2.39.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Connect-MgGraph | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.51.1.0", + "packageVersionCandidate": "1.51.1", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "contributor": "Microsoft.Graph.Authentication", + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "assemblyName": "System.ClientModel", + "assemblyVersion": "1.9.0.0", + "packageVersionCandidate": "1.9.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "contributor": "Microsoft.Graph.Authentication", + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + } + ] + }, + { + "name": "MicrosoftTeams", + "umbrellaModule": "MicrosoftTeams", + "constituentModule": "MicrosoftTeams", + "version": "7.9.0", + "latestCompatibleVersion": "7.9.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Microsoft.Identity.Client", + "assemblyVersion": "4.82.0.0", + "packageVersionCandidate": "4.82.0", + "fullName": "Microsoft.Identity.Client, Version=4.82.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "MicrosoftTeams", + "selectedAsset": "upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll" + } + ] + } + ], + "conflictMatrix": { + "conflictSurface": [ + "Azure.Core", + "System.ClientModel" + ], + "assemblies": [ + { + "name": "Azure.Core", + "shippedBy": [ + "Az.Accounts", + "Az.Resources", + "Az.Storage", + "Microsoft.Graph.Authentication" + ], + "versions": [ + "1.51.1.0", + "1.57.0.0" + ], + "hashes": [ + "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext", + "msgraph-load-context" + ], + "diverges": true, + "selections": [ + { + "contributor": "Az.Accounts", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Resources", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Storage", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Microsoft.Graph.Authentication", + "version": "1.51.1.0", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context" + } + ] + }, + { + "name": "Microsoft.Extensions.DependencyInjection.Abstractions", + "shippedBy": [ + "Az.Resources" + ], + "versions": [ + "10.0.0.0" + ], + "hashes": [ + "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Resources", + "version": "10.0.0.0", + "sha256": "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Identity.Client", + "shippedBy": [ + "MicrosoftTeams" + ], + "versions": [ + "4.82.0.0" + ], + "hashes": [ + "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "MicrosoftTeams", + "version": "4.82.0.0", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "shippedBy": [ + "Az.Accounts", + "Az.Resources", + "Az.Storage" + ], + "versions": [ + "4.84.0.0" + ], + "hashes": [ + "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Accounts", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Resources", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Storage", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + } + ] + }, + { + "name": "Microsoft.OData.Core", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.OData.Edm", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Spatial", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "System.ClientModel", + "shippedBy": [ + "Az.Storage", + "Microsoft.Graph.Authentication" + ], + "versions": [ + "1.13.0.0", + "1.9.0.0" + ], + "hashes": [ + "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext", + "msgraph-load-context" + ], + "diverges": true, + "selections": [ + { + "contributor": "Az.Storage", + "version": "1.13.0.0", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Microsoft.Graph.Authentication", + "version": "1.9.0.0", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context" + } + ] + } + ] + }, + "scenarios": [ + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 3, + "importOrder": [ + "Az.Storage", + "ExchangeOnlineManagement" + ], + "importedModuleAssets": [ + "upstream:Az.Storage/9.7.2/Az.Storage.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-Command Get-AzStorageAccount | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 3, + "importOrder": [ + "Az.Storage", + "ExchangeOnlineManagement" + ], + "importedModuleAssets": [ + "upstream:Az.Storage/9.7.2/Az.Storage.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-Command Get-AzStorageAccount | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "93a9d9734d735eeab95fdbf96814ae0805b859737f64f031eec9d073dbe8dade", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "43d776a30d70705669c36ae476d06b28428a9f7708bbc7c9533392c9a027b132", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "e120f7147f5b55c4d8f549b7263cbd30ca141da013cd904892fa3cab4e8c15f2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "761ca72eefb9acb33a1d96328d0484f31b3dc68f696743e130877d05d8d97bf2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bdab1d4e9806c4b2ad6162647ec29b9e909247936d785ccbbdd0196ffbf169bf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 4, + "importOrder": [ + "ExchangeOnlineManagement", + "Az.Storage" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:Az.Storage/9.7.2/Az.Storage.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Get-AzStorageAccount | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 4, + "importOrder": [ + "ExchangeOnlineManagement", + "Az.Storage" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:Az.Storage/9.7.2/Az.Storage.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Get-AzStorageAccount | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "93a9d9734d735eeab95fdbf96814ae0805b859737f64f031eec9d073dbe8dade", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "43d776a30d70705669c36ae476d06b28428a9f7708bbc7c9533392c9a027b132", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "e120f7147f5b55c4d8f549b7263cbd30ca141da013cd904892fa3cab4e8c15f2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "761ca72eefb9acb33a1d96328d0484f31b3dc68f696743e130877d05d8d97bf2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bdab1d4e9806c4b2ad6162647ec29b9e909247936d785ccbbdd0196ffbf169bf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-01", + "orderIndex": 1, + "importOrder": [ + "ExchangeOnlineManagement", + "MicrosoftTeams", + "Microsoft.Graph.Authentication", + "Az.Accounts" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.51.1.0", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.82.0.0", + "fullName": "Microsoft.Identity.Client, Version=4.82.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "System.ClientModel", + "version": "1.9.0.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-01", + "orderIndex": 1, + "importOrder": [ + "ExchangeOnlineManagement", + "MicrosoftTeams", + "Microsoft.Graph.Authentication", + "Az.Accounts" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.51.1.0", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "93a9d9734d735eeab95fdbf96814ae0805b859737f64f031eec9d073dbe8dade", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "43d776a30d70705669c36ae476d06b28428a9f7708bbc7c9533392c9a027b132", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "e120f7147f5b55c4d8f549b7263cbd30ca141da013cd904892fa3cab4e8c15f2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "761ca72eefb9acb33a1d96328d0484f31b3dc68f696743e130877d05d8d97bf2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "System.ClientModel", + "version": "1.9.0.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bdab1d4e9806c4b2ad6162647ec29b9e909247936d785ccbbdd0196ffbf169bf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-02", + "orderIndex": 2, + "importOrder": [ + "Az.Accounts", + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "MicrosoftTeams" + ], + "importedModuleAssets": [ + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "d18da88bce62a54fa71b3cdf9dfa4b4dfc9759521e872721535d5d5ff63a7651", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-02", + "orderIndex": 2, + "importOrder": [ + "Az.Accounts", + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "MicrosoftTeams" + ], + "importedModuleAssets": [ + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "93a9d9734d735eeab95fdbf96814ae0805b859737f64f031eec9d073dbe8dade", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "43d776a30d70705669c36ae476d06b28428a9f7708bbc7c9533392c9a027b132", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "e120f7147f5b55c4d8f549b7263cbd30ca141da013cd904892fa3cab4e8c15f2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "761ca72eefb9acb33a1d96328d0484f31b3dc68f696743e130877d05d8d97bf2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bdab1d4e9806c4b2ad6162647ec29b9e909247936d785ccbbdd0196ffbf169bf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + } + ] + } +} diff --git a/build/profile-evidence/ps7.5-net9.0-macos-x64.json b/build/profile-evidence/ps7.5-net9.0-macos-x64.json new file mode 100644 index 00000000..3b04a956 --- /dev/null +++ b/build/profile-evidence/ps7.5-net9.0-macos-x64.json @@ -0,0 +1,1415 @@ +{ + "schemaVersion": 1, + "contentFingerprint": "dd70b37eec9f32c08c75198544dbc9f40635ea3046287d835c9d0c5074a6831d", + "provenance": { + "sourceRunId": "31349870045", + "sourceRunUrl": "https://github.com/SamErde/DLLPickle/actions/runs/31349870045", + "sourceCommitSha": "586b1fed1189d9a34e6b054dbffa07b62a706d97", + "capturedAtUtc": "2026-08-10T02:31:03.0000000+00:00", + "observedOperatingSystems": [ + "macOS 15.7.7" + ] + }, + "content": { + "profile": { + "profileKey": "ps7.5-net9.0-macos-x64", + "powerShellVersion": "7.5.9", + "powerShellLine": "7.5", + "dotNetVersion": "9.0.18", + "dotNetMajor": 9, + "targetFramework": "net9.0", + "platform": "macos", + "architecture": "x64" + }, + "validation": { + "deterministicImportNoAuth": { + "status": "passed", + "writesPerformed": false, + "conflictSurfaceFingerprint": "1cc79f2f3af66f0d4b1793e5311df21066980435e89c75ba710a2f643b9884d7", + "scenarioFingerprint": "4e39d071eda5c4fa9be584bb1805a0932c0fc8113e1f27194aa6760d3a3f541a" + }, + "authenticatedReadOnly": { + "status": "not-run-no-approved-credentials", + "writesPerformed": false, + "unexecutedCommands": [ + "Get-MgContext | Out-Null", + "Get-EXOMailbox -ResultSize 1 | Out-Null", + "Get-AzStorageAccount | Select-Object -First 1 | Out-Null", + "Get-AzContext | Out-Null", + "Connect-MicrosoftTeams -AccessTokens @($env:DLLPICKLE_GRAPH_ACCESS_TOKEN, $env:DLLPICKLE_TEAMS_ACCESS_TOKEN) | Out-Null; try { Get-CsTenant | Out-Null } finally { Disconnect-MicrosoftTeams | Out-Null }", + "Get-AzResource | Select-Object -First 1 | Out-Null" + ] + } + }, + "modules": [ + { + "name": "Az.Accounts", + "umbrellaModule": "Az", + "constituentModule": "Az.Accounts", + "version": "5.5.2", + "latestCompatibleVersion": "5.5.2", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + } + ] + }, + { + "name": "Az.Resources", + "umbrellaModule": "Az", + "constituentModule": "Az.Resources", + "version": "10.1.0", + "latestCompatibleVersion": "10.1.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Get-AzResource | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Extensions.DependencyInjection.Abstractions", + "assemblyVersion": "10.0.0.0", + "packageVersionCandidate": "10.0.0", + "fullName": "Microsoft.Extensions.DependencyInjection.Abstractions, Version=10.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "sha256": "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Resources", + "selectedAsset": "upstream:Az.Resources/10.1.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + } + ] + }, + { + "name": "Az.Storage", + "umbrellaModule": "Az", + "constituentModule": "Az.Storage", + "version": "9.7.2", + "latestCompatibleVersion": "9.7.2", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Get-AzStorageAccount | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "assemblyName": "Microsoft.OData.Core", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "assemblyName": "Microsoft.OData.Edm", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "assemblyName": "Microsoft.Spatial", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "assemblyName": "System.ClientModel", + "assemblyVersion": "1.13.0.0", + "packageVersionCandidate": "1.13.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "name": "ExchangeOnlineManagement", + "umbrellaModule": "ExchangeOnlineManagement", + "constituentModule": "ExchangeOnlineManagement", + "version": "3.10.1", + "latestCompatibleVersion": "3.10.1", + "repository": "PSGallery", + "manifestPowerShellVersion": "3.0", + "compatiblePSEditions": [], + "deterministicProbeCommand": "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "selectedAssets": [] + }, + { + "name": "Microsoft.Graph.Authentication", + "umbrellaModule": "Microsoft.Graph", + "constituentModule": "Microsoft.Graph.Authentication", + "version": "2.39.0", + "latestCompatibleVersion": "2.39.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Connect-MgGraph | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.51.1.0", + "packageVersionCandidate": "1.51.1", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "contributor": "Microsoft.Graph.Authentication", + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "assemblyName": "System.ClientModel", + "assemblyVersion": "1.9.0.0", + "packageVersionCandidate": "1.9.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "contributor": "Microsoft.Graph.Authentication", + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + } + ] + }, + { + "name": "MicrosoftTeams", + "umbrellaModule": "MicrosoftTeams", + "constituentModule": "MicrosoftTeams", + "version": "7.9.0", + "latestCompatibleVersion": "7.9.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Microsoft.Identity.Client", + "assemblyVersion": "4.82.0.0", + "packageVersionCandidate": "4.82.0", + "fullName": "Microsoft.Identity.Client, Version=4.82.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "MicrosoftTeams", + "selectedAsset": "upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll" + } + ] + } + ], + "conflictMatrix": { + "conflictSurface": [ + "Azure.Core", + "System.ClientModel" + ], + "assemblies": [ + { + "name": "Azure.Core", + "shippedBy": [ + "Az.Accounts", + "Az.Resources", + "Az.Storage", + "Microsoft.Graph.Authentication" + ], + "versions": [ + "1.51.1.0", + "1.57.0.0" + ], + "hashes": [ + "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext", + "msgraph-load-context" + ], + "diverges": true, + "selections": [ + { + "contributor": "Az.Accounts", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Resources", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Storage", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Microsoft.Graph.Authentication", + "version": "1.51.1.0", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context" + } + ] + }, + { + "name": "Microsoft.Extensions.DependencyInjection.Abstractions", + "shippedBy": [ + "Az.Resources" + ], + "versions": [ + "10.0.0.0" + ], + "hashes": [ + "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Resources", + "version": "10.0.0.0", + "sha256": "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Identity.Client", + "shippedBy": [ + "MicrosoftTeams" + ], + "versions": [ + "4.82.0.0" + ], + "hashes": [ + "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "MicrosoftTeams", + "version": "4.82.0.0", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "shippedBy": [ + "Az.Accounts", + "Az.Resources", + "Az.Storage" + ], + "versions": [ + "4.84.0.0" + ], + "hashes": [ + "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Accounts", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Resources", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Storage", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + } + ] + }, + { + "name": "Microsoft.OData.Core", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.OData.Edm", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Spatial", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "System.ClientModel", + "shippedBy": [ + "Az.Storage", + "Microsoft.Graph.Authentication" + ], + "versions": [ + "1.13.0.0", + "1.9.0.0" + ], + "hashes": [ + "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext", + "msgraph-load-context" + ], + "diverges": true, + "selections": [ + { + "contributor": "Az.Storage", + "version": "1.13.0.0", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Microsoft.Graph.Authentication", + "version": "1.9.0.0", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context" + } + ] + } + ] + }, + "scenarios": [ + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 3, + "importOrder": [ + "Az.Storage", + "ExchangeOnlineManagement" + ], + "importedModuleAssets": [ + "upstream:Az.Storage/9.7.2/Az.Storage.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-Command Get-AzStorageAccount | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 3, + "importOrder": [ + "Az.Storage", + "ExchangeOnlineManagement" + ], + "importedModuleAssets": [ + "upstream:Az.Storage/9.7.2/Az.Storage.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-Command Get-AzStorageAccount | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "93a9d9734d735eeab95fdbf96814ae0805b859737f64f031eec9d073dbe8dade", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "43d776a30d70705669c36ae476d06b28428a9f7708bbc7c9533392c9a027b132", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "e120f7147f5b55c4d8f549b7263cbd30ca141da013cd904892fa3cab4e8c15f2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "761ca72eefb9acb33a1d96328d0484f31b3dc68f696743e130877d05d8d97bf2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bdab1d4e9806c4b2ad6162647ec29b9e909247936d785ccbbdd0196ffbf169bf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 4, + "importOrder": [ + "ExchangeOnlineManagement", + "Az.Storage" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:Az.Storage/9.7.2/Az.Storage.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Get-AzStorageAccount | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 4, + "importOrder": [ + "ExchangeOnlineManagement", + "Az.Storage" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:Az.Storage/9.7.2/Az.Storage.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Get-AzStorageAccount | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "93a9d9734d735eeab95fdbf96814ae0805b859737f64f031eec9d073dbe8dade", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "43d776a30d70705669c36ae476d06b28428a9f7708bbc7c9533392c9a027b132", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "e120f7147f5b55c4d8f549b7263cbd30ca141da013cd904892fa3cab4e8c15f2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "761ca72eefb9acb33a1d96328d0484f31b3dc68f696743e130877d05d8d97bf2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bdab1d4e9806c4b2ad6162647ec29b9e909247936d785ccbbdd0196ffbf169bf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-01", + "orderIndex": 1, + "importOrder": [ + "ExchangeOnlineManagement", + "MicrosoftTeams", + "Microsoft.Graph.Authentication", + "Az.Accounts" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.51.1.0", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.82.0.0", + "fullName": "Microsoft.Identity.Client, Version=4.82.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "System.ClientModel", + "version": "1.9.0.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-01", + "orderIndex": 1, + "importOrder": [ + "ExchangeOnlineManagement", + "MicrosoftTeams", + "Microsoft.Graph.Authentication", + "Az.Accounts" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.51.1.0", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "93a9d9734d735eeab95fdbf96814ae0805b859737f64f031eec9d073dbe8dade", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "43d776a30d70705669c36ae476d06b28428a9f7708bbc7c9533392c9a027b132", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "e120f7147f5b55c4d8f549b7263cbd30ca141da013cd904892fa3cab4e8c15f2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "761ca72eefb9acb33a1d96328d0484f31b3dc68f696743e130877d05d8d97bf2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "System.ClientModel", + "version": "1.9.0.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bdab1d4e9806c4b2ad6162647ec29b9e909247936d785ccbbdd0196ffbf169bf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-02", + "orderIndex": 2, + "importOrder": [ + "Az.Accounts", + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "MicrosoftTeams" + ], + "importedModuleAssets": [ + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "d18da88bce62a54fa71b3cdf9dfa4b4dfc9759521e872721535d5d5ff63a7651", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-02", + "orderIndex": 2, + "importOrder": [ + "Az.Accounts", + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "MicrosoftTeams" + ], + "importedModuleAssets": [ + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "93a9d9734d735eeab95fdbf96814ae0805b859737f64f031eec9d073dbe8dade", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "43d776a30d70705669c36ae476d06b28428a9f7708bbc7c9533392c9a027b132", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "e120f7147f5b55c4d8f549b7263cbd30ca141da013cd904892fa3cab4e8c15f2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "761ca72eefb9acb33a1d96328d0484f31b3dc68f696743e130877d05d8d97bf2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bdab1d4e9806c4b2ad6162647ec29b9e909247936d785ccbbdd0196ffbf169bf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + } + ] + } +} diff --git a/build/profile-evidence/ps7.5-net9.0-windows-x64.json b/build/profile-evidence/ps7.5-net9.0-windows-x64.json new file mode 100644 index 00000000..d8b36436 --- /dev/null +++ b/build/profile-evidence/ps7.5-net9.0-windows-x64.json @@ -0,0 +1,1558 @@ +{ + "schemaVersion": 1, + "contentFingerprint": "ac62ce357b779bddc117d6234d1fbf3edcbc09bfa755053f1ce32d97dbe8dc4f", + "provenance": { + "sourceRunId": "31349870045", + "sourceRunUrl": "https://github.com/SamErde/DLLPickle/actions/runs/31349870045", + "sourceCommitSha": "586b1fed1189d9a34e6b054dbffa07b62a706d97", + "capturedAtUtc": "2026-08-10T02:30:33.0000000+00:00", + "observedOperatingSystems": [ + "Microsoft Windows 10.0.26100" + ] + }, + "content": { + "profile": { + "profileKey": "ps7.5-net9.0-windows-x64", + "powerShellVersion": "7.5.9", + "powerShellLine": "7.5", + "dotNetVersion": "9.0.18", + "dotNetMajor": 9, + "targetFramework": "net9.0", + "platform": "windows", + "architecture": "x64" + }, + "validation": { + "deterministicImportNoAuth": { + "status": "passed", + "writesPerformed": false, + "conflictSurfaceFingerprint": "335a11b2b849902e213614891c43dcf864f40d193ee587e10dc58a6ccd40d729", + "scenarioFingerprint": "36684a8b67017021ed7540ccc1c4eaaf2d4a53a1a4c807f4433f78c7d324d001" + }, + "authenticatedReadOnly": { + "status": "not-run-no-approved-credentials", + "writesPerformed": false, + "unexecutedCommands": [ + "Get-MgContext | Out-Null", + "Get-EXOMailbox -ResultSize 1 | Out-Null", + "Get-AzStorageAccount | Select-Object -First 1 | Out-Null", + "Get-AzContext | Out-Null", + "Connect-MicrosoftTeams -AccessTokens @($env:DLLPICKLE_GRAPH_ACCESS_TOKEN, $env:DLLPICKLE_TEAMS_ACCESS_TOKEN) | Out-Null; try { Get-CsTenant | Out-Null } finally { Disconnect-MicrosoftTeams | Out-Null }", + "Get-AzResource | Select-Object -First 1 | Out-Null" + ] + } + }, + "modules": [ + { + "name": "Az.Accounts", + "umbrellaModule": "Az", + "constituentModule": "Az.Accounts", + "version": "5.5.2", + "latestCompatibleVersion": "5.5.2", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "assemblyName": "System.Security.Cryptography.ProtectedData", + "assemblyVersion": "9.0.0.0", + "packageVersionCandidate": "9.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eee01276c62b271b9e77f434a854920c33fd45b4415d1c5512cbfa048b275a9c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "name": "Az.Resources", + "umbrellaModule": "Az", + "constituentModule": "Az.Resources", + "version": "10.1.0", + "latestCompatibleVersion": "10.1.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Get-AzResource | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Extensions.DependencyInjection.Abstractions", + "assemblyVersion": "10.0.0.0", + "packageVersionCandidate": "10.0.0", + "fullName": "Microsoft.Extensions.DependencyInjection.Abstractions, Version=10.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "sha256": "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Resources", + "selectedAsset": "upstream:Az.Resources/10.1.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "assemblyName": "System.Security.Cryptography.ProtectedData", + "assemblyVersion": "9.0.0.0", + "packageVersionCandidate": "9.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eee01276c62b271b9e77f434a854920c33fd45b4415d1c5512cbfa048b275a9c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Resources", + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "name": "Az.Storage", + "umbrellaModule": "Az", + "constituentModule": "Az.Storage", + "version": "9.7.2", + "latestCompatibleVersion": "9.7.2", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Get-AzStorageAccount | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "assemblyName": "Microsoft.OData.Core", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "assemblyName": "Microsoft.OData.Edm", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "assemblyName": "Microsoft.Spatial", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "assemblyName": "System.ClientModel", + "assemblyVersion": "1.13.0.0", + "packageVersionCandidate": "1.13.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "assemblyName": "System.Security.Cryptography.ProtectedData", + "assemblyVersion": "9.0.0.0", + "packageVersionCandidate": "9.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eee01276c62b271b9e77f434a854920c33fd45b4415d1c5512cbfa048b275a9c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "name": "ExchangeOnlineManagement", + "umbrellaModule": "ExchangeOnlineManagement", + "constituentModule": "ExchangeOnlineManagement", + "version": "3.10.1", + "latestCompatibleVersion": "3.10.1", + "repository": "PSGallery", + "manifestPowerShellVersion": "3.0", + "compatiblePSEditions": [], + "deterministicProbeCommand": "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "selectedAssets": [] + }, + { + "name": "Microsoft.Graph.Authentication", + "umbrellaModule": "Microsoft.Graph", + "constituentModule": "Microsoft.Graph.Authentication", + "version": "2.39.0", + "latestCompatibleVersion": "2.39.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Connect-MgGraph | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.51.1.0", + "packageVersionCandidate": "1.51.1", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "contributor": "Microsoft.Graph.Authentication", + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "assemblyName": "System.ClientModel", + "assemblyVersion": "1.9.0.0", + "packageVersionCandidate": "1.9.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "contributor": "Microsoft.Graph.Authentication", + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + } + ] + }, + { + "name": "MicrosoftTeams", + "umbrellaModule": "MicrosoftTeams", + "constituentModule": "MicrosoftTeams", + "version": "7.9.0", + "latestCompatibleVersion": "7.9.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Microsoft.Identity.Client", + "assemblyVersion": "4.82.0.0", + "packageVersionCandidate": "4.82.0", + "fullName": "Microsoft.Identity.Client, Version=4.82.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "MicrosoftTeams", + "selectedAsset": "upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll" + } + ] + } + ], + "conflictMatrix": { + "conflictSurface": [ + "Azure.Core", + "System.ClientModel" + ], + "assemblies": [ + { + "name": "Azure.Core", + "shippedBy": [ + "Az.Accounts", + "Az.Resources", + "Az.Storage", + "Microsoft.Graph.Authentication" + ], + "versions": [ + "1.51.1.0", + "1.57.0.0" + ], + "hashes": [ + "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext", + "msgraph-load-context" + ], + "diverges": true, + "selections": [ + { + "contributor": "Az.Accounts", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Resources", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Storage", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Microsoft.Graph.Authentication", + "version": "1.51.1.0", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context" + } + ] + }, + { + "name": "Microsoft.Extensions.DependencyInjection.Abstractions", + "shippedBy": [ + "Az.Resources" + ], + "versions": [ + "10.0.0.0" + ], + "hashes": [ + "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Resources", + "version": "10.0.0.0", + "sha256": "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Identity.Client", + "shippedBy": [ + "MicrosoftTeams" + ], + "versions": [ + "4.82.0.0" + ], + "hashes": [ + "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "MicrosoftTeams", + "version": "4.82.0.0", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "shippedBy": [ + "Az.Accounts", + "Az.Resources", + "Az.Storage" + ], + "versions": [ + "4.84.0.0" + ], + "hashes": [ + "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Accounts", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Resources", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Storage", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + } + ] + }, + { + "name": "Microsoft.OData.Core", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.OData.Edm", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Spatial", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "System.ClientModel", + "shippedBy": [ + "Az.Storage", + "Microsoft.Graph.Authentication" + ], + "versions": [ + "1.13.0.0", + "1.9.0.0" + ], + "hashes": [ + "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext", + "msgraph-load-context" + ], + "diverges": true, + "selections": [ + { + "contributor": "Az.Storage", + "version": "1.13.0.0", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Microsoft.Graph.Authentication", + "version": "1.9.0.0", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context" + } + ] + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "shippedBy": [ + "Az.Accounts", + "Az.Resources", + "Az.Storage" + ], + "versions": [ + "9.0.0.0" + ], + "hashes": [ + "eee01276c62b271b9e77f434a854920c33fd45b4415d1c5512cbfa048b275a9c" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Accounts", + "version": "9.0.0.0", + "sha256": "eee01276c62b271b9e77f434a854920c33fd45b4415d1c5512cbfa048b275a9c", + "assemblyLoadContext": "Default" + }, + { + "contributor": "Az.Resources", + "version": "9.0.0.0", + "sha256": "eee01276c62b271b9e77f434a854920c33fd45b4415d1c5512cbfa048b275a9c", + "assemblyLoadContext": "Default" + }, + { + "contributor": "Az.Storage", + "version": "9.0.0.0", + "sha256": "eee01276c62b271b9e77f434a854920c33fd45b4415d1c5512cbfa048b275a9c", + "assemblyLoadContext": "Default" + } + ] + } + ] + }, + "scenarios": [ + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 3, + "importOrder": [ + "Az.Storage", + "ExchangeOnlineManagement" + ], + "importedModuleAssets": [ + "upstream:Az.Storage/9.7.2/Az.Storage.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-Command Get-AzStorageAccount | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "9.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eee01276c62b271b9e77f434a854920c33fd45b4415d1c5512cbfa048b275a9c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 3, + "importOrder": [ + "Az.Storage", + "ExchangeOnlineManagement" + ], + "importedModuleAssets": [ + "upstream:Az.Storage/9.7.2/Az.Storage.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-Command Get-AzStorageAccount | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "93a9d9734d735eeab95fdbf96814ae0805b859737f64f031eec9d073dbe8dade", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "43d776a30d70705669c36ae476d06b28428a9f7708bbc7c9533392c9a027b132", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "e120f7147f5b55c4d8f549b7263cbd30ca141da013cd904892fa3cab4e8c15f2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "761ca72eefb9acb33a1d96328d0484f31b3dc68f696743e130877d05d8d97bf2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bdab1d4e9806c4b2ad6162647ec29b9e909247936d785ccbbdd0196ffbf169bf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/System.IdentityModel.Tokens.Jwt.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "9.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eee01276c62b271b9e77f434a854920c33fd45b4415d1c5512cbfa048b275a9c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 4, + "importOrder": [ + "ExchangeOnlineManagement", + "Az.Storage" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:Az.Storage/9.7.2/Az.Storage.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Get-AzStorageAccount | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "9.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eee01276c62b271b9e77f434a854920c33fd45b4415d1c5512cbfa048b275a9c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 4, + "importOrder": [ + "ExchangeOnlineManagement", + "Az.Storage" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:Az.Storage/9.7.2/Az.Storage.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Get-AzStorageAccount | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "93a9d9734d735eeab95fdbf96814ae0805b859737f64f031eec9d073dbe8dade", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "43d776a30d70705669c36ae476d06b28428a9f7708bbc7c9533392c9a027b132", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "e120f7147f5b55c4d8f549b7263cbd30ca141da013cd904892fa3cab4e8c15f2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "761ca72eefb9acb33a1d96328d0484f31b3dc68f696743e130877d05d8d97bf2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bdab1d4e9806c4b2ad6162647ec29b9e909247936d785ccbbdd0196ffbf169bf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/System.IdentityModel.Tokens.Jwt.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "9.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eee01276c62b271b9e77f434a854920c33fd45b4415d1c5512cbfa048b275a9c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-01", + "orderIndex": 1, + "importOrder": [ + "ExchangeOnlineManagement", + "MicrosoftTeams", + "Microsoft.Graph.Authentication", + "Az.Accounts" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.51.1.0", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.82.0.0", + "fullName": "Microsoft.Identity.Client, Version=4.82.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "System.ClientModel", + "version": "1.9.0.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "9.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eee01276c62b271b9e77f434a854920c33fd45b4415d1c5512cbfa048b275a9c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-01", + "orderIndex": 1, + "importOrder": [ + "ExchangeOnlineManagement", + "MicrosoftTeams", + "Microsoft.Graph.Authentication", + "Az.Accounts" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.51.1.0", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "93a9d9734d735eeab95fdbf96814ae0805b859737f64f031eec9d073dbe8dade", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "43d776a30d70705669c36ae476d06b28428a9f7708bbc7c9533392c9a027b132", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "e120f7147f5b55c4d8f549b7263cbd30ca141da013cd904892fa3cab4e8c15f2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "761ca72eefb9acb33a1d96328d0484f31b3dc68f696743e130877d05d8d97bf2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "System.ClientModel", + "version": "1.9.0.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bdab1d4e9806c4b2ad6162647ec29b9e909247936d785ccbbdd0196ffbf169bf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/System.IdentityModel.Tokens.Jwt.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "9.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eee01276c62b271b9e77f434a854920c33fd45b4415d1c5512cbfa048b275a9c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-02", + "orderIndex": 2, + "importOrder": [ + "Az.Accounts", + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "MicrosoftTeams" + ], + "importedModuleAssets": [ + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "d18da88bce62a54fa71b3cdf9dfa4b4dfc9759521e872721535d5d5ff63a7651", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "9.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eee01276c62b271b9e77f434a854920c33fd45b4415d1c5512cbfa048b275a9c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-02", + "orderIndex": 2, + "importOrder": [ + "Az.Accounts", + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "MicrosoftTeams" + ], + "importedModuleAssets": [ + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "93a9d9734d735eeab95fdbf96814ae0805b859737f64f031eec9d073dbe8dade", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "43d776a30d70705669c36ae476d06b28428a9f7708bbc7c9533392c9a027b132", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "e120f7147f5b55c4d8f549b7263cbd30ca141da013cd904892fa3cab4e8c15f2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "761ca72eefb9acb33a1d96328d0484f31b3dc68f696743e130877d05d8d97bf2", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "bdab1d4e9806c4b2ad6162647ec29b9e909247936d785ccbbdd0196ffbf169bf", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net9.0/System.IdentityModel.Tokens.Jwt.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "9.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eee01276c62b271b9e77f434a854920c33fd45b4415d1c5512cbfa048b275a9c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + } + ] + } +} diff --git a/build/profile-evidence/ps7.6-net10.0-linux-x64.json b/build/profile-evidence/ps7.6-net10.0-linux-x64.json new file mode 100644 index 00000000..47a02f77 --- /dev/null +++ b/build/profile-evidence/ps7.6-net10.0-linux-x64.json @@ -0,0 +1,1415 @@ +{ + "schemaVersion": 1, + "contentFingerprint": "8f7e16648edefdfdf0f4f7edb09984c5fb43788363a770f8f36ce594a40938c8", + "provenance": { + "sourceRunId": "31349870045", + "sourceRunUrl": "https://github.com/SamErde/DLLPickle/actions/runs/31349870045", + "sourceCommitSha": "586b1fed1189d9a34e6b054dbffa07b62a706d97", + "capturedAtUtc": "2026-08-10T02:30:10.0000000+00:00", + "observedOperatingSystems": [ + "Ubuntu 24.04.4 LTS" + ] + }, + "content": { + "profile": { + "profileKey": "ps7.6-net10.0-linux-x64", + "powerShellVersion": "7.6.4", + "powerShellLine": "7.6", + "dotNetVersion": "10.0.10", + "dotNetMajor": 10, + "targetFramework": "net10.0", + "platform": "linux", + "architecture": "x64" + }, + "validation": { + "deterministicImportNoAuth": { + "status": "passed", + "writesPerformed": false, + "conflictSurfaceFingerprint": "96c2933d465d2c94b3f7caf53095788bed1ea51dd4a46290a88ec5a4a60ca643", + "scenarioFingerprint": "9ce72aabd07190fbe7f42070b45756b2d9297256357e0ae0798978ae14265f73" + }, + "authenticatedReadOnly": { + "status": "not-run-no-approved-credentials", + "writesPerformed": false, + "unexecutedCommands": [ + "Get-MgContext | Out-Null", + "Get-EXOMailbox -ResultSize 1 | Out-Null", + "Get-AzStorageAccount | Select-Object -First 1 | Out-Null", + "Get-AzContext | Out-Null", + "Connect-MicrosoftTeams -AccessTokens @($env:DLLPICKLE_GRAPH_ACCESS_TOKEN, $env:DLLPICKLE_TEAMS_ACCESS_TOKEN) | Out-Null; try { Get-CsTenant | Out-Null } finally { Disconnect-MicrosoftTeams | Out-Null }", + "Get-AzResource | Select-Object -First 1 | Out-Null" + ] + } + }, + "modules": [ + { + "name": "Az.Accounts", + "umbrellaModule": "Az", + "constituentModule": "Az.Accounts", + "version": "5.5.2", + "latestCompatibleVersion": "5.5.2", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + } + ] + }, + { + "name": "Az.Resources", + "umbrellaModule": "Az", + "constituentModule": "Az.Resources", + "version": "10.1.0", + "latestCompatibleVersion": "10.1.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Get-AzResource | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Extensions.DependencyInjection.Abstractions", + "assemblyVersion": "10.0.0.0", + "packageVersionCandidate": "10.0.0", + "fullName": "Microsoft.Extensions.DependencyInjection.Abstractions, Version=10.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "sha256": "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Resources", + "selectedAsset": "upstream:Az.Resources/10.1.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + } + ] + }, + { + "name": "Az.Storage", + "umbrellaModule": "Az", + "constituentModule": "Az.Storage", + "version": "9.7.2", + "latestCompatibleVersion": "9.7.2", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Get-AzStorageAccount | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "assemblyName": "Microsoft.OData.Core", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "assemblyName": "Microsoft.OData.Edm", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "assemblyName": "Microsoft.Spatial", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "assemblyName": "System.ClientModel", + "assemblyVersion": "1.13.0.0", + "packageVersionCandidate": "1.13.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "name": "ExchangeOnlineManagement", + "umbrellaModule": "ExchangeOnlineManagement", + "constituentModule": "ExchangeOnlineManagement", + "version": "3.10.1", + "latestCompatibleVersion": "3.10.1", + "repository": "PSGallery", + "manifestPowerShellVersion": "3.0", + "compatiblePSEditions": [], + "deterministicProbeCommand": "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "selectedAssets": [] + }, + { + "name": "Microsoft.Graph.Authentication", + "umbrellaModule": "Microsoft.Graph", + "constituentModule": "Microsoft.Graph.Authentication", + "version": "2.39.0", + "latestCompatibleVersion": "2.39.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Connect-MgGraph | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.51.1.0", + "packageVersionCandidate": "1.51.1", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "contributor": "Microsoft.Graph.Authentication", + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "assemblyName": "System.ClientModel", + "assemblyVersion": "1.9.0.0", + "packageVersionCandidate": "1.9.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "contributor": "Microsoft.Graph.Authentication", + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + } + ] + }, + { + "name": "MicrosoftTeams", + "umbrellaModule": "MicrosoftTeams", + "constituentModule": "MicrosoftTeams", + "version": "7.9.0", + "latestCompatibleVersion": "7.9.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Microsoft.Identity.Client", + "assemblyVersion": "4.82.0.0", + "packageVersionCandidate": "4.82.0", + "fullName": "Microsoft.Identity.Client, Version=4.82.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "MicrosoftTeams", + "selectedAsset": "upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll" + } + ] + } + ], + "conflictMatrix": { + "conflictSurface": [ + "Azure.Core", + "System.ClientModel" + ], + "assemblies": [ + { + "name": "Azure.Core", + "shippedBy": [ + "Az.Accounts", + "Az.Resources", + "Az.Storage", + "Microsoft.Graph.Authentication" + ], + "versions": [ + "1.51.1.0", + "1.57.0.0" + ], + "hashes": [ + "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext", + "msgraph-load-context" + ], + "diverges": true, + "selections": [ + { + "contributor": "Az.Accounts", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Resources", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Storage", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Microsoft.Graph.Authentication", + "version": "1.51.1.0", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context" + } + ] + }, + { + "name": "Microsoft.Extensions.DependencyInjection.Abstractions", + "shippedBy": [ + "Az.Resources" + ], + "versions": [ + "10.0.0.0" + ], + "hashes": [ + "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Resources", + "version": "10.0.0.0", + "sha256": "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Identity.Client", + "shippedBy": [ + "MicrosoftTeams" + ], + "versions": [ + "4.82.0.0" + ], + "hashes": [ + "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "MicrosoftTeams", + "version": "4.82.0.0", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "shippedBy": [ + "Az.Accounts", + "Az.Resources", + "Az.Storage" + ], + "versions": [ + "4.84.0.0" + ], + "hashes": [ + "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Accounts", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Resources", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Storage", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + } + ] + }, + { + "name": "Microsoft.OData.Core", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.OData.Edm", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Spatial", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "System.ClientModel", + "shippedBy": [ + "Az.Storage", + "Microsoft.Graph.Authentication" + ], + "versions": [ + "1.13.0.0", + "1.9.0.0" + ], + "hashes": [ + "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext", + "msgraph-load-context" + ], + "diverges": true, + "selections": [ + { + "contributor": "Az.Storage", + "version": "1.13.0.0", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Microsoft.Graph.Authentication", + "version": "1.9.0.0", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context" + } + ] + } + ] + }, + "scenarios": [ + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 3, + "importOrder": [ + "Az.Storage", + "ExchangeOnlineManagement" + ], + "importedModuleAssets": [ + "upstream:Az.Storage/9.7.2/Az.Storage.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-Command Get-AzStorageAccount | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 3, + "importOrder": [ + "Az.Storage", + "ExchangeOnlineManagement" + ], + "importedModuleAssets": [ + "upstream:Az.Storage/9.7.2/Az.Storage.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-Command Get-AzStorageAccount | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "f2f55ce6fb73d2685f092d6867d3c16f754ba823680f1f5678b221bbe6495548", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "2979cb64cd9a2de74660108177318e0fd8412bb5a0db8d672f204de6d24b8f8e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "9f255afec8099c977f88f35321faa1bf0bd0de2365d38ef1b1b929be20849898", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0c0dfdf8ff226127873c475cd82697a20d5fb28ebbf12918062cfdb317d3ed79", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "58a929a34e6fffa201a9f8e1e1f3ce6d8530685e341a45b085a9b6d2015eb96d", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 4, + "importOrder": [ + "ExchangeOnlineManagement", + "Az.Storage" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:Az.Storage/9.7.2/Az.Storage.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Get-AzStorageAccount | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 4, + "importOrder": [ + "ExchangeOnlineManagement", + "Az.Storage" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:Az.Storage/9.7.2/Az.Storage.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Get-AzStorageAccount | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "f2f55ce6fb73d2685f092d6867d3c16f754ba823680f1f5678b221bbe6495548", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "2979cb64cd9a2de74660108177318e0fd8412bb5a0db8d672f204de6d24b8f8e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "9f255afec8099c977f88f35321faa1bf0bd0de2365d38ef1b1b929be20849898", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0c0dfdf8ff226127873c475cd82697a20d5fb28ebbf12918062cfdb317d3ed79", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "58a929a34e6fffa201a9f8e1e1f3ce6d8530685e341a45b085a9b6d2015eb96d", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-01", + "orderIndex": 1, + "importOrder": [ + "ExchangeOnlineManagement", + "MicrosoftTeams", + "Microsoft.Graph.Authentication", + "Az.Accounts" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.51.1.0", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.82.0.0", + "fullName": "Microsoft.Identity.Client, Version=4.82.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "System.ClientModel", + "version": "1.9.0.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-01", + "orderIndex": 1, + "importOrder": [ + "ExchangeOnlineManagement", + "MicrosoftTeams", + "Microsoft.Graph.Authentication", + "Az.Accounts" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.51.1.0", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "f2f55ce6fb73d2685f092d6867d3c16f754ba823680f1f5678b221bbe6495548", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "2979cb64cd9a2de74660108177318e0fd8412bb5a0db8d672f204de6d24b8f8e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "9f255afec8099c977f88f35321faa1bf0bd0de2365d38ef1b1b929be20849898", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0c0dfdf8ff226127873c475cd82697a20d5fb28ebbf12918062cfdb317d3ed79", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "System.ClientModel", + "version": "1.9.0.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "58a929a34e6fffa201a9f8e1e1f3ce6d8530685e341a45b085a9b6d2015eb96d", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-02", + "orderIndex": 2, + "importOrder": [ + "Az.Accounts", + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "MicrosoftTeams" + ], + "importedModuleAssets": [ + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "d18da88bce62a54fa71b3cdf9dfa4b4dfc9759521e872721535d5d5ff63a7651", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-02", + "orderIndex": 2, + "importOrder": [ + "Az.Accounts", + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "MicrosoftTeams" + ], + "importedModuleAssets": [ + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "f2f55ce6fb73d2685f092d6867d3c16f754ba823680f1f5678b221bbe6495548", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "2979cb64cd9a2de74660108177318e0fd8412bb5a0db8d672f204de6d24b8f8e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "9f255afec8099c977f88f35321faa1bf0bd0de2365d38ef1b1b929be20849898", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0c0dfdf8ff226127873c475cd82697a20d5fb28ebbf12918062cfdb317d3ed79", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "58a929a34e6fffa201a9f8e1e1f3ce6d8530685e341a45b085a9b6d2015eb96d", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + } + ] + } +} diff --git a/build/profile-evidence/ps7.6-net10.0-macos-x64.json b/build/profile-evidence/ps7.6-net10.0-macos-x64.json new file mode 100644 index 00000000..cdb83cd5 --- /dev/null +++ b/build/profile-evidence/ps7.6-net10.0-macos-x64.json @@ -0,0 +1,1415 @@ +{ + "schemaVersion": 1, + "contentFingerprint": "631d8a315d5b16a3b417e2089cb6daf0629125986b3eec82335e8f4b283b8ea7", + "provenance": { + "sourceRunId": "31349870045", + "sourceRunUrl": "https://github.com/SamErde/DLLPickle/actions/runs/31349870045", + "sourceCommitSha": "586b1fed1189d9a34e6b054dbffa07b62a706d97", + "capturedAtUtc": "2026-08-10T02:32:33.0000000+00:00", + "observedOperatingSystems": [ + "macOS 15.7.7" + ] + }, + "content": { + "profile": { + "profileKey": "ps7.6-net10.0-macos-x64", + "powerShellVersion": "7.6.4", + "powerShellLine": "7.6", + "dotNetVersion": "10.0.10", + "dotNetMajor": 10, + "targetFramework": "net10.0", + "platform": "macos", + "architecture": "x64" + }, + "validation": { + "deterministicImportNoAuth": { + "status": "passed", + "writesPerformed": false, + "conflictSurfaceFingerprint": "e7f221fbab3f96e5cf10fb6c21d811e56d1b58b0aa079c8c4b1869573442ba56", + "scenarioFingerprint": "9c23ccd74eed68a1ef25d4d9da3b39ed797d445b1ba1ffb4db5b6d65469e55e6" + }, + "authenticatedReadOnly": { + "status": "not-run-no-approved-credentials", + "writesPerformed": false, + "unexecutedCommands": [ + "Get-MgContext | Out-Null", + "Get-EXOMailbox -ResultSize 1 | Out-Null", + "Get-AzStorageAccount | Select-Object -First 1 | Out-Null", + "Get-AzContext | Out-Null", + "Connect-MicrosoftTeams -AccessTokens @($env:DLLPICKLE_GRAPH_ACCESS_TOKEN, $env:DLLPICKLE_TEAMS_ACCESS_TOKEN) | Out-Null; try { Get-CsTenant | Out-Null } finally { Disconnect-MicrosoftTeams | Out-Null }", + "Get-AzResource | Select-Object -First 1 | Out-Null" + ] + } + }, + "modules": [ + { + "name": "Az.Accounts", + "umbrellaModule": "Az", + "constituentModule": "Az.Accounts", + "version": "5.5.2", + "latestCompatibleVersion": "5.5.2", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + } + ] + }, + { + "name": "Az.Resources", + "umbrellaModule": "Az", + "constituentModule": "Az.Resources", + "version": "10.1.0", + "latestCompatibleVersion": "10.1.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Get-AzResource | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Extensions.DependencyInjection.Abstractions", + "assemblyVersion": "10.0.0.0", + "packageVersionCandidate": "10.0.0", + "fullName": "Microsoft.Extensions.DependencyInjection.Abstractions, Version=10.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "sha256": "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Resources", + "selectedAsset": "upstream:Az.Resources/10.1.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + } + ] + }, + { + "name": "Az.Storage", + "umbrellaModule": "Az", + "constituentModule": "Az.Storage", + "version": "9.7.2", + "latestCompatibleVersion": "9.7.2", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Get-AzStorageAccount | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "assemblyName": "Microsoft.OData.Core", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "assemblyName": "Microsoft.OData.Edm", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "assemblyName": "Microsoft.Spatial", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "assemblyName": "System.ClientModel", + "assemblyVersion": "1.13.0.0", + "packageVersionCandidate": "1.13.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "name": "ExchangeOnlineManagement", + "umbrellaModule": "ExchangeOnlineManagement", + "constituentModule": "ExchangeOnlineManagement", + "version": "3.10.1", + "latestCompatibleVersion": "3.10.1", + "repository": "PSGallery", + "manifestPowerShellVersion": "3.0", + "compatiblePSEditions": [], + "deterministicProbeCommand": "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "selectedAssets": [] + }, + { + "name": "Microsoft.Graph.Authentication", + "umbrellaModule": "Microsoft.Graph", + "constituentModule": "Microsoft.Graph.Authentication", + "version": "2.39.0", + "latestCompatibleVersion": "2.39.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Connect-MgGraph | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.51.1.0", + "packageVersionCandidate": "1.51.1", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "contributor": "Microsoft.Graph.Authentication", + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "assemblyName": "System.ClientModel", + "assemblyVersion": "1.9.0.0", + "packageVersionCandidate": "1.9.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "contributor": "Microsoft.Graph.Authentication", + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + } + ] + }, + { + "name": "MicrosoftTeams", + "umbrellaModule": "MicrosoftTeams", + "constituentModule": "MicrosoftTeams", + "version": "7.9.0", + "latestCompatibleVersion": "7.9.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Microsoft.Identity.Client", + "assemblyVersion": "4.82.0.0", + "packageVersionCandidate": "4.82.0", + "fullName": "Microsoft.Identity.Client, Version=4.82.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "MicrosoftTeams", + "selectedAsset": "upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll" + } + ] + } + ], + "conflictMatrix": { + "conflictSurface": [ + "Azure.Core", + "System.ClientModel" + ], + "assemblies": [ + { + "name": "Azure.Core", + "shippedBy": [ + "Az.Accounts", + "Az.Resources", + "Az.Storage", + "Microsoft.Graph.Authentication" + ], + "versions": [ + "1.51.1.0", + "1.57.0.0" + ], + "hashes": [ + "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext", + "msgraph-load-context" + ], + "diverges": true, + "selections": [ + { + "contributor": "Az.Accounts", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Resources", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Storage", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Microsoft.Graph.Authentication", + "version": "1.51.1.0", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context" + } + ] + }, + { + "name": "Microsoft.Extensions.DependencyInjection.Abstractions", + "shippedBy": [ + "Az.Resources" + ], + "versions": [ + "10.0.0.0" + ], + "hashes": [ + "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Resources", + "version": "10.0.0.0", + "sha256": "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Identity.Client", + "shippedBy": [ + "MicrosoftTeams" + ], + "versions": [ + "4.82.0.0" + ], + "hashes": [ + "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "MicrosoftTeams", + "version": "4.82.0.0", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "shippedBy": [ + "Az.Accounts", + "Az.Resources", + "Az.Storage" + ], + "versions": [ + "4.84.0.0" + ], + "hashes": [ + "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Accounts", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Resources", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Storage", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + } + ] + }, + { + "name": "Microsoft.OData.Core", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.OData.Edm", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Spatial", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "System.ClientModel", + "shippedBy": [ + "Az.Storage", + "Microsoft.Graph.Authentication" + ], + "versions": [ + "1.13.0.0", + "1.9.0.0" + ], + "hashes": [ + "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext", + "msgraph-load-context" + ], + "diverges": true, + "selections": [ + { + "contributor": "Az.Storage", + "version": "1.13.0.0", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Microsoft.Graph.Authentication", + "version": "1.9.0.0", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context" + } + ] + } + ] + }, + "scenarios": [ + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 3, + "importOrder": [ + "Az.Storage", + "ExchangeOnlineManagement" + ], + "importedModuleAssets": [ + "upstream:Az.Storage/9.7.2/Az.Storage.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-Command Get-AzStorageAccount | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 3, + "importOrder": [ + "Az.Storage", + "ExchangeOnlineManagement" + ], + "importedModuleAssets": [ + "upstream:Az.Storage/9.7.2/Az.Storage.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-Command Get-AzStorageAccount | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "f2f55ce6fb73d2685f092d6867d3c16f754ba823680f1f5678b221bbe6495548", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "2979cb64cd9a2de74660108177318e0fd8412bb5a0db8d672f204de6d24b8f8e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "9f255afec8099c977f88f35321faa1bf0bd0de2365d38ef1b1b929be20849898", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0c0dfdf8ff226127873c475cd82697a20d5fb28ebbf12918062cfdb317d3ed79", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "58a929a34e6fffa201a9f8e1e1f3ce6d8530685e341a45b085a9b6d2015eb96d", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 4, + "importOrder": [ + "ExchangeOnlineManagement", + "Az.Storage" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:Az.Storage/9.7.2/Az.Storage.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Get-AzStorageAccount | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 4, + "importOrder": [ + "ExchangeOnlineManagement", + "Az.Storage" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:Az.Storage/9.7.2/Az.Storage.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Get-AzStorageAccount | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "f2f55ce6fb73d2685f092d6867d3c16f754ba823680f1f5678b221bbe6495548", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "2979cb64cd9a2de74660108177318e0fd8412bb5a0db8d672f204de6d24b8f8e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "9f255afec8099c977f88f35321faa1bf0bd0de2365d38ef1b1b929be20849898", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0c0dfdf8ff226127873c475cd82697a20d5fb28ebbf12918062cfdb317d3ed79", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "58a929a34e6fffa201a9f8e1e1f3ce6d8530685e341a45b085a9b6d2015eb96d", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-01", + "orderIndex": 1, + "importOrder": [ + "ExchangeOnlineManagement", + "MicrosoftTeams", + "Microsoft.Graph.Authentication", + "Az.Accounts" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.51.1.0", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.82.0.0", + "fullName": "Microsoft.Identity.Client, Version=4.82.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "System.ClientModel", + "version": "1.9.0.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-01", + "orderIndex": 1, + "importOrder": [ + "ExchangeOnlineManagement", + "MicrosoftTeams", + "Microsoft.Graph.Authentication", + "Az.Accounts" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.51.1.0", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "f2f55ce6fb73d2685f092d6867d3c16f754ba823680f1f5678b221bbe6495548", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "2979cb64cd9a2de74660108177318e0fd8412bb5a0db8d672f204de6d24b8f8e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "9f255afec8099c977f88f35321faa1bf0bd0de2365d38ef1b1b929be20849898", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0c0dfdf8ff226127873c475cd82697a20d5fb28ebbf12918062cfdb317d3ed79", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "System.ClientModel", + "version": "1.9.0.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "58a929a34e6fffa201a9f8e1e1f3ce6d8530685e341a45b085a9b6d2015eb96d", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-02", + "orderIndex": 2, + "importOrder": [ + "Az.Accounts", + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "MicrosoftTeams" + ], + "importedModuleAssets": [ + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "d18da88bce62a54fa71b3cdf9dfa4b4dfc9759521e872721535d5d5ff63a7651", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-02", + "orderIndex": 2, + "importOrder": [ + "Az.Accounts", + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "MicrosoftTeams" + ], + "importedModuleAssets": [ + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "f2f55ce6fb73d2685f092d6867d3c16f754ba823680f1f5678b221bbe6495548", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "2979cb64cd9a2de74660108177318e0fd8412bb5a0db8d672f204de6d24b8f8e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "9f255afec8099c977f88f35321faa1bf0bd0de2365d38ef1b1b929be20849898", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0c0dfdf8ff226127873c475cd82697a20d5fb28ebbf12918062cfdb317d3ed79", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "58a929a34e6fffa201a9f8e1e1f3ce6d8530685e341a45b085a9b6d2015eb96d", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/System.IdentityModel.Tokens.Jwt.dll" + } + ] + } + ] + } +} diff --git a/build/profile-evidence/ps7.6-net10.0-windows-x64.json b/build/profile-evidence/ps7.6-net10.0-windows-x64.json new file mode 100644 index 00000000..445e1b5f --- /dev/null +++ b/build/profile-evidence/ps7.6-net10.0-windows-x64.json @@ -0,0 +1,1558 @@ +{ + "schemaVersion": 1, + "contentFingerprint": "a9f7107ef9360feef67f1eca1fb7afcabdb217f1c14b6415bdcbba76df07a48e", + "provenance": { + "sourceRunId": "31349870045", + "sourceRunUrl": "https://github.com/SamErde/DLLPickle/actions/runs/31349870045", + "sourceCommitSha": "586b1fed1189d9a34e6b054dbffa07b62a706d97", + "capturedAtUtc": "2026-08-10T02:31:00.0000000+00:00", + "observedOperatingSystems": [ + "Microsoft Windows 10.0.26100" + ] + }, + "content": { + "profile": { + "profileKey": "ps7.6-net10.0-windows-x64", + "powerShellVersion": "7.6.4", + "powerShellLine": "7.6", + "dotNetVersion": "10.0.10", + "dotNetMajor": 10, + "targetFramework": "net10.0", + "platform": "windows", + "architecture": "x64" + }, + "validation": { + "deterministicImportNoAuth": { + "status": "passed", + "writesPerformed": false, + "conflictSurfaceFingerprint": "1c5b37d886f13577fa1380c11ede9dc225fe517a281103b8076a8924d90389f3", + "scenarioFingerprint": "ff71ef233c126de350e6d01de17838c3963ebc16dcbad5b1598a03ffd12b57b7" + }, + "authenticatedReadOnly": { + "status": "not-run-no-approved-credentials", + "writesPerformed": false, + "unexecutedCommands": [ + "Get-MgContext | Out-Null", + "Get-EXOMailbox -ResultSize 1 | Out-Null", + "Get-AzStorageAccount | Select-Object -First 1 | Out-Null", + "Get-AzContext | Out-Null", + "Connect-MicrosoftTeams -AccessTokens @($env:DLLPICKLE_GRAPH_ACCESS_TOKEN, $env:DLLPICKLE_TEAMS_ACCESS_TOKEN) | Out-Null; try { Get-CsTenant | Out-Null } finally { Disconnect-MicrosoftTeams | Out-Null }", + "Get-AzResource | Select-Object -First 1 | Out-Null" + ] + } + }, + "modules": [ + { + "name": "Az.Accounts", + "umbrellaModule": "Az", + "constituentModule": "Az.Accounts", + "version": "5.5.2", + "latestCompatibleVersion": "5.5.2", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "assemblyName": "System.Security.Cryptography.ProtectedData", + "assemblyVersion": "10.0.0.0", + "packageVersionCandidate": "10.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eb535b4eaaed42bd93e1cea9e03e25480d7371f6b1f2452216e9a8aff262ae5f", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "name": "Az.Resources", + "umbrellaModule": "Az", + "constituentModule": "Az.Resources", + "version": "10.1.0", + "latestCompatibleVersion": "10.1.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Get-AzResource | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Extensions.DependencyInjection.Abstractions", + "assemblyVersion": "10.0.0.0", + "packageVersionCandidate": "10.0.0", + "fullName": "Microsoft.Extensions.DependencyInjection.Abstractions, Version=10.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "sha256": "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Resources", + "selectedAsset": "upstream:Az.Resources/10.1.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "assemblyName": "System.Security.Cryptography.ProtectedData", + "assemblyVersion": "10.0.0.0", + "packageVersionCandidate": "10.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eb535b4eaaed42bd93e1cea9e03e25480d7371f6b1f2452216e9a8aff262ae5f", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Resources", + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "name": "Az.Storage", + "umbrellaModule": "Az", + "constituentModule": "Az.Storage", + "version": "9.7.2", + "latestCompatibleVersion": "9.7.2", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Get-AzStorageAccount | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.57.0.0", + "packageVersionCandidate": "1.57.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "assemblyName": "Microsoft.Identity.Client.Extensions.Msal", + "assemblyVersion": "4.84.0.0", + "packageVersionCandidate": "4.84.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "assemblyName": "Microsoft.OData.Core", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "assemblyName": "Microsoft.OData.Edm", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "assemblyName": "Microsoft.Spatial", + "assemblyVersion": "7.6.4.0", + "packageVersionCandidate": "7.6.4", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "assemblyName": "System.ClientModel", + "assemblyVersion": "1.13.0.0", + "packageVersionCandidate": "1.13.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "contributor": "Az.Accounts", + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "assemblyName": "System.Security.Cryptography.ProtectedData", + "assemblyVersion": "10.0.0.0", + "packageVersionCandidate": "10.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eb535b4eaaed42bd93e1cea9e03e25480d7371f6b1f2452216e9a8aff262ae5f", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "Az.Storage", + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "name": "ExchangeOnlineManagement", + "umbrellaModule": "ExchangeOnlineManagement", + "constituentModule": "ExchangeOnlineManagement", + "version": "3.10.1", + "latestCompatibleVersion": "3.10.1", + "repository": "PSGallery", + "manifestPowerShellVersion": "3.0", + "compatiblePSEditions": [], + "deterministicProbeCommand": "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "selectedAssets": [] + }, + { + "name": "Microsoft.Graph.Authentication", + "umbrellaModule": "Microsoft.Graph", + "constituentModule": "Microsoft.Graph.Authentication", + "version": "2.39.0", + "latestCompatibleVersion": "2.39.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Command Connect-MgGraph | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Azure.Core", + "assemblyVersion": "1.51.1.0", + "packageVersionCandidate": "1.51.1", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "contributor": "Microsoft.Graph.Authentication", + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "assemblyName": "System.ClientModel", + "assemblyVersion": "1.9.0.0", + "packageVersionCandidate": "1.9.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "contributor": "Microsoft.Graph.Authentication", + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + } + ] + }, + { + "name": "MicrosoftTeams", + "umbrellaModule": "MicrosoftTeams", + "constituentModule": "MicrosoftTeams", + "version": "7.9.0", + "latestCompatibleVersion": "7.9.0", + "repository": "PSGallery", + "manifestPowerShellVersion": "5.1", + "compatiblePSEditions": [ + "Core", + "Desktop" + ], + "deterministicProbeCommand": "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "selectedAssets": [ + { + "assemblyName": "Microsoft.Identity.Client", + "assemblyVersion": "4.82.0.0", + "packageVersionCandidate": "4.82.0", + "fullName": "Microsoft.Identity.Client, Version=4.82.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default", + "isCollectible": false, + "contributor": "MicrosoftTeams", + "selectedAsset": "upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll" + } + ] + } + ], + "conflictMatrix": { + "conflictSurface": [ + "Azure.Core", + "System.ClientModel" + ], + "assemblies": [ + { + "name": "Azure.Core", + "shippedBy": [ + "Az.Accounts", + "Az.Resources", + "Az.Storage", + "Microsoft.Graph.Authentication" + ], + "versions": [ + "1.51.1.0", + "1.57.0.0" + ], + "hashes": [ + "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext", + "msgraph-load-context" + ], + "diverges": true, + "selections": [ + { + "contributor": "Az.Accounts", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Resources", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Storage", + "version": "1.57.0.0", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Microsoft.Graph.Authentication", + "version": "1.51.1.0", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context" + } + ] + }, + { + "name": "Microsoft.Extensions.DependencyInjection.Abstractions", + "shippedBy": [ + "Az.Resources" + ], + "versions": [ + "10.0.0.0" + ], + "hashes": [ + "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Resources", + "version": "10.0.0.0", + "sha256": "63a83951b4d871f9487da5192bd1acb15f156c9cbd8d399e38a3b3574991f3ad", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Identity.Client", + "shippedBy": [ + "MicrosoftTeams" + ], + "versions": [ + "4.82.0.0" + ], + "hashes": [ + "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "MicrosoftTeams", + "version": "4.82.0.0", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "shippedBy": [ + "Az.Accounts", + "Az.Resources", + "Az.Storage" + ], + "versions": [ + "4.84.0.0" + ], + "hashes": [ + "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Accounts", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Resources", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Az.Storage", + "version": "4.84.0.0", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + } + ] + }, + { + "name": "Microsoft.OData.Core", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.OData.Edm", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "Microsoft.Spatial", + "shippedBy": [ + "Az.Storage" + ], + "versions": [ + "7.6.4.0" + ], + "hashes": [ + "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Storage", + "version": "7.6.4.0", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default" + } + ] + }, + { + "name": "System.ClientModel", + "shippedBy": [ + "Az.Storage", + "Microsoft.Graph.Authentication" + ], + "versions": [ + "1.13.0.0", + "1.9.0.0" + ], + "hashes": [ + "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065" + ], + "assemblyLoadContexts": [ + "AzSharedAssemblyLoadContext", + "msgraph-load-context" + ], + "diverges": true, + "selections": [ + { + "contributor": "Az.Storage", + "version": "1.13.0.0", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext" + }, + { + "contributor": "Microsoft.Graph.Authentication", + "version": "1.9.0.0", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context" + } + ] + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "shippedBy": [ + "Az.Accounts", + "Az.Resources", + "Az.Storage" + ], + "versions": [ + "10.0.0.0" + ], + "hashes": [ + "eb535b4eaaed42bd93e1cea9e03e25480d7371f6b1f2452216e9a8aff262ae5f" + ], + "assemblyLoadContexts": [ + "Default" + ], + "diverges": false, + "selections": [ + { + "contributor": "Az.Accounts", + "version": "10.0.0.0", + "sha256": "eb535b4eaaed42bd93e1cea9e03e25480d7371f6b1f2452216e9a8aff262ae5f", + "assemblyLoadContext": "Default" + }, + { + "contributor": "Az.Resources", + "version": "10.0.0.0", + "sha256": "eb535b4eaaed42bd93e1cea9e03e25480d7371f6b1f2452216e9a8aff262ae5f", + "assemblyLoadContext": "Default" + }, + { + "contributor": "Az.Storage", + "version": "10.0.0.0", + "sha256": "eb535b4eaaed42bd93e1cea9e03e25480d7371f6b1f2452216e9a8aff262ae5f", + "assemblyLoadContext": "Default" + } + ] + } + ] + }, + "scenarios": [ + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 3, + "importOrder": [ + "Az.Storage", + "ExchangeOnlineManagement" + ], + "importedModuleAssets": [ + "upstream:Az.Storage/9.7.2/Az.Storage.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-Command Get-AzStorageAccount | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "10.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eb535b4eaaed42bd93e1cea9e03e25480d7371f6b1f2452216e9a8aff262ae5f", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 3, + "importOrder": [ + "Az.Storage", + "ExchangeOnlineManagement" + ], + "importedModuleAssets": [ + "upstream:Az.Storage/9.7.2/Az.Storage.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-Command Get-AzStorageAccount | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "f2f55ce6fb73d2685f092d6867d3c16f754ba823680f1f5678b221bbe6495548", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "2979cb64cd9a2de74660108177318e0fd8412bb5a0db8d672f204de6d24b8f8e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "9f255afec8099c977f88f35321faa1bf0bd0de2365d38ef1b1b929be20849898", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0c0dfdf8ff226127873c475cd82697a20d5fb28ebbf12918062cfdb317d3ed79", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "58a929a34e6fffa201a9f8e1e1f3ce6d8530685e341a45b085a9b6d2015eb96d", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/System.IdentityModel.Tokens.Jwt.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "10.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eb535b4eaaed42bd93e1cea9e03e25480d7371f6b1f2452216e9a8aff262ae5f", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 4, + "importOrder": [ + "ExchangeOnlineManagement", + "Az.Storage" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:Az.Storage/9.7.2/Az.Storage.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Get-AzStorageAccount | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "10.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eb535b4eaaed42bd93e1cea9e03e25480d7371f6b1f2452216e9a8aff262ae5f", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "scenarioId": "174-odata-azstorage-exo", + "orderIndex": 4, + "importOrder": [ + "ExchangeOnlineManagement", + "Az.Storage" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:Az.Storage/9.7.2/Az.Storage.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": true, + "expectedSuccess": null, + "outcomePolicy": "observe-known-limitation", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Get-AzStorageAccount | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "f2f55ce6fb73d2685f092d6867d3c16f754ba823680f1f5678b221bbe6495548", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "2979cb64cd9a2de74660108177318e0fd8412bb5a0db8d672f204de6d24b8f8e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "9f255afec8099c977f88f35321faa1bf0bd0de2365d38ef1b1b929be20849898", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0c0dfdf8ff226127873c475cd82697a20d5fb28ebbf12918062cfdb317d3ed79", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "Microsoft.OData.Core", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Core, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "1eeaf431ad2fbb81cb052e1d59ee9d853668df14b435bd53b3bc0ab2dbbe430c", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll" + }, + { + "name": "Microsoft.OData.Edm", + "version": "7.6.4.0", + "fullName": "Microsoft.OData.Edm, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "92496b441126eaf6581d824641ad230f323e3f8f883100b2e8d3ea36aeef0b99", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll" + }, + { + "name": "Microsoft.Spatial", + "version": "7.6.4.0", + "fullName": "Microsoft.Spatial, Version=7.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "df1bf09898ecb028dfa5dfcaefe8ca4d75f3c57c8a98c1eeffe4b9b71a49436e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "58a929a34e6fffa201a9f8e1e1f3ce6d8530685e341a45b085a9b6d2015eb96d", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/System.IdentityModel.Tokens.Jwt.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "10.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eb535b4eaaed42bd93e1cea9e03e25480d7371f6b1f2452216e9a8aff262ae5f", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-01", + "orderIndex": 1, + "importOrder": [ + "ExchangeOnlineManagement", + "MicrosoftTeams", + "Microsoft.Graph.Authentication", + "Az.Accounts" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.51.1.0", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.82.0.0", + "fullName": "Microsoft.Identity.Client, Version=4.82.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "35b5b56e0c0a1fef35e7865943cd8d8bec25a7facc225d01078ca6fd7b41b9a9", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "System.ClientModel", + "version": "1.9.0.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "10.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eb535b4eaaed42bd93e1cea9e03e25480d7371f6b1f2452216e9a8aff262ae5f", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-01", + "orderIndex": 1, + "importOrder": [ + "ExchangeOnlineManagement", + "MicrosoftTeams", + "Microsoft.Graph.Authentication", + "Az.Accounts" + ], + "importedModuleAssets": [ + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.51.1.0", + "fullName": "Azure.Core, Version=1.51.1.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "f734878c493959695f189f6d52d72d9a4601152e1486950895b0628160784cb5", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll" + }, + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "f2f55ce6fb73d2685f092d6867d3c16f754ba823680f1f5678b221bbe6495548", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "2979cb64cd9a2de74660108177318e0fd8412bb5a0db8d672f204de6d24b8f8e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "9f255afec8099c977f88f35321faa1bf0bd0de2365d38ef1b1b929be20849898", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0c0dfdf8ff226127873c475cd82697a20d5fb28ebbf12918062cfdb317d3ed79", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "System.ClientModel", + "version": "1.9.0.0", + "fullName": "System.ClientModel, Version=1.9.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "0b49fd44c751de7a9fd7eb0a3a1104fbfb3bd5d8d616f4af5b3ca6c98a8dd614", + "assemblyLoadContext": "msgraph-load-context", + "isCollectible": false, + "selectedAsset": "upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "58a929a34e6fffa201a9f8e1e1f3ce6d8530685e341a45b085a9b6d2015eb96d", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/System.IdentityModel.Tokens.Jwt.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "10.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eb535b4eaaed42bd93e1cea9e03e25480d7371f6b1f2452216e9a8aff262ae5f", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-02", + "orderIndex": 2, + "importOrder": [ + "Az.Accounts", + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "MicrosoftTeams" + ], + "importedModuleAssets": [ + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1" + ], + "dllPicklePreloaded": false, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "d18da88bce62a54fa71b3cdf9dfa4b4dfc9759521e872721535d5d5ff63a7651", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.0.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.0.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "10f799a3ff80ccffc2b0a9dc9534465ef68deb5de79340b63bf20c5a58e6bbda", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "10.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eb535b4eaaed42bd93e1cea9e03e25480d7371f6b1f2452216e9a8aff262ae5f", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + }, + { + "scenarioId": "profile-target-scenario-02", + "orderIndex": 2, + "importOrder": [ + "Az.Accounts", + "Microsoft.Graph.Authentication", + "ExchangeOnlineManagement", + "MicrosoftTeams" + ], + "importedModuleAssets": [ + "upstream:Az.Accounts/5.5.2/Az.Accounts.psd1", + "upstream:Microsoft.Graph.Authentication/2.39.0/Microsoft.Graph.Authentication.psd1", + "upstream:ExchangeOnlineManagement/3.10.1/ExchangeOnlineManagement.psd1", + "upstream:MicrosoftTeams/7.9.0/MicrosoftTeams.psd1" + ], + "dllPicklePreloaded": true, + "expectedLimitation": false, + "expectedSuccess": true, + "outcomePolicy": "must-succeed", + "probeCommands": [ + "Get-AzContext -ErrorAction SilentlyContinue | Out-Null", + "Get-Command Connect-MgGraph | Out-Null", + "Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null", + "Get-Team -ErrorAction SilentlyContinue | Select-Object -First 1 | Out-Null" + ], + "success": true, + "outcomeMatchesExpectation": true, + "errorObserved": false, + "assemblies": [ + { + "name": "Azure.Core", + "version": "1.57.0.0", + "fullName": "Azure.Core, Version=1.57.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "7ec9feb7738364d2d3a7a598f7c0cff090fdff971086d45cfb35a67858c6f5c7", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll" + }, + { + "name": "Microsoft.Identity.Client", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "32832e3ebbde8bf119987998f04741b878d3a399d17c285d4091a0c400c04ede", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.dll" + }, + { + "name": "Microsoft.Identity.Client.Broker", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Broker, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "3d7707aa40fc4f3fc033f7ab1f97ab5b9625f0a116d847ec4e8b3cab0c6fb614", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Broker.dll" + }, + { + "name": "Microsoft.Identity.Client.Extensions.Msal", + "version": "4.84.1.0", + "fullName": "Microsoft.Identity.Client.Extensions.Msal, Version=4.84.1.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "47d07553ea5759bcfdc14fc65f02ca873f781e08713df235b01ea95a8543adb3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.Extensions.Msal.dll" + }, + { + "name": "Microsoft.Identity.Client.NativeInterop", + "version": "0.20.6.0", + "fullName": "Microsoft.Identity.Client.NativeInterop, Version=0.20.6.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae", + "sha256": "ab9315edf6dcc91f26f20bce51e11b75d5bd4e673b44a3784cf3752555bbdbd3", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.Identity.Client.NativeInterop.dll" + }, + { + "name": "Microsoft.IdentityModel.Abstractions", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Abstractions, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "f2f55ce6fb73d2685f092d6867d3c16f754ba823680f1f5678b221bbe6495548", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Abstractions.dll" + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.JsonWebTokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "2979cb64cd9a2de74660108177318e0fd8412bb5a0db8d672f204de6d24b8f8e", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.JsonWebTokens.dll" + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Logging, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "9f255afec8099c977f88f35321faa1bf0bd0de2365d38ef1b1b929be20849898", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Logging.dll" + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "8.18.0.0", + "fullName": "Microsoft.IdentityModel.Tokens, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "0c0dfdf8ff226127873c475cd82697a20d5fb28ebbf12918062cfdb317d3ed79", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/Microsoft.IdentityModel.Tokens.dll" + }, + { + "name": "System.ClientModel", + "version": "1.13.0.0", + "fullName": "System.ClientModel, Version=1.13.0.0, Culture=neutral, PublicKeyToken=92742159e12e44c8", + "sha256": "113a90cac8ff3cee04e52960f52897d5c0d2c40a1f05f3ae04289498c7bb7065", + "assemblyLoadContext": "AzSharedAssemblyLoadContext", + "isCollectible": false, + "selectedAsset": "upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll" + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "8.18.0.0", + "fullName": "System.IdentityModel.Tokens.Jwt, Version=8.18.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + "sha256": "58a929a34e6fffa201a9f8e1e1f3ce6d8530685e341a45b085a9b6d2015eb96d", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "dllpickle:bin/net10.0/System.IdentityModel.Tokens.Jwt.dll" + }, + { + "name": "System.Security.Cryptography.ProtectedData", + "version": "10.0.0.0", + "fullName": "System.Security.Cryptography.ProtectedData, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "sha256": "eb535b4eaaed42bd93e1cea9e03e25480d7371f6b1f2452216e9a8aff262ae5f", + "assemblyLoadContext": "Default", + "isCollectible": false, + "selectedAsset": "runtime:System.Security.Cryptography.ProtectedData.dll" + } + ] + } + ] + } +} diff --git a/docs/Architecture.md b/docs/Architecture.md index 71ac49ae..e786a5e9 100644 --- a/docs/Architecture.md +++ b/docs/Architecture.md @@ -13,17 +13,18 @@ DLLPickle ships two distinct kinds of capability, and they have **different runt | Tier | Functions | Runtime requirement | What it does | | --- | --- | --- | --- | -| **Preloader (automated fix)** | `Import-DPLibrary`, `Import-DPBaseProfile` | **PowerShell 7.4+ / .NET 8 (`net8.0`) only.** Depends on `AssemblyLoadContext`. | Loads the bundled `bin/net8.0` identity stack into the default ALC so later module loads reuse one coherent version. | +| **Preloader (automated fix)** | `Import-DPLibrary`, `Import-DPBaseProfile` | The exact Microsoft-supported profiles in the [generated support matrix](generated/Support-Matrix.md): PowerShell 7.4 / `net8.0`, 7.5 / `net9.0`, and 7.6 / `net10.0`. Depends on `AssemblyLoadContext`. | Selects the isolated bundle using both the PowerShell minor and CLR major, then loads it into the default ALC so later module loads reuse one coherent version. | | **Inspection / diagnostics (manual aid)** | `Find-DLLInPSModulePath`, `Get-ModuleImportCandidate`, `Get-ModulesWithDependency`, `Get-ModulesWithVersionSortedIdentityClient`, `Test-DPLibraryConflict` (plus `Get-DPConfig`/`Set-DPConfig`) | **Cross-edition by design.** Deliberately also inspects Windows PowerShell module roots, including `Documents\WindowsPowerShell\Modules`; the all-users WinPS root is auto-seeded only when actually running on 5.1. | Reports which installed module ships the newest identity DLL, so a user can decide which service to connect to first. | The inspection tier exists to serve the project charter even for environments the preloader cannot reach: a **Windows PowerShell 5.1** user who hits the same version-conflict problem can run the inspection helpers to discover which module to load first and apply that **manual** "first-one-wins" workaround. See §1.2 for the precise platform-support contract. ### 1.2 Platform-support contract (decisions 1 & 2) -- **The automated preloader targets PowerShell 7.4+ on the `net8.0` runtime profile, and only that.** This is the supported way to *fix* the conflict automatically. It is enforced top to bottom: the manifest declares `PowerShellVersion = '7.4'` and `CompatiblePSEditions = @('Core')` (`src/DLLPickle/DLLPickle.psd1`); the build sets `RequiredPSVersion = '7.4.0'` (`build/DLLPickle.Settings.ps1`); the build project is single-target `net8.0` (`src/DLLPickle.Build/DLLPickle.csproj`); and `Import-DPLibrary` hard-codes `bin/net8.0` and throws if that directory is missing. +- **The automated preloader supports exactly the declared PowerShell/CLR profiles, not a floating “7.4 or later” range.** `src/DLLPickle/SupportedRuntimeProfiles.json` is the shipped behavior map; `build/powershell-test-matrix.json` supplies exact servicing patches and lifecycle evidence for CI. The project targets `net8.0;net9.0;net10.0`, and `Get-DPRuntimeProfile` requires the PowerShell minor, CLR major, and target framework to agree before `Import-DPLibrary` loads the matching `bin/` directory. Unknown, duplicate, malformed, or mismatched profiles fail closed. +- **Servicing patches and support-contract changes have different automation.** A newer patch within a declared line is checksum- and runtime-validated across the exact OS matrix, then published once on a fingerprint-derived automation branch. A new minor line, lifecycle-date change, impending retirement, or expired line opens or updates a deduplicated review issue; it never silently adds or removes a shipped TFM. Release publication fails closed while an expired line remains claimed. - **Windows PowerShell 5.1 / .NET Framework 4.8 is not a supported runtime for the preloader, by design.** The whole self-isolation reasoning in §2 depends on `AssemblyLoadContext`, which does not exist on .NET Framework 4.8. The module manifest is `Core`-only, so DLLPickle is not intended to be *imported and run* under Windows PowerShell 5.1. -- **The inspection/diagnostic tier is deliberately cross-edition.** Its functions are written to run on both editions and to scan the **Windows PowerShell module roots** that the current session can identify (for example `Documents\WindowsPowerShell\Modules`, plus the all-users WinPS root when actually running on 5.1) as well as the PowerShell 7 roots. This is intentional: a 5.1 user who needs a *manual* solution to the version-conflict problem can use these helpers to identify which module to connect to first. The supported way to do this today is to run the helpers **from a PowerShell 7.4+ session** while they inspect a machine's installed modules, understanding that the all-users WinPS root is only auto-seeded when the helper itself runs on 5.1 (see the §10 note on the manifest edition declaration). -- **Multi-TFM (net9.0/net10.0) is a deferred goal**, not current scope (§10). The methodology is TFM-parameterizable; the runtime profile is single-target `net8.0` today. +- **The inspection/diagnostic tier is deliberately cross-edition.** Its functions are written to run on both editions and to scan the **Windows PowerShell module roots** that the current session can identify (for example `Documents\WindowsPowerShell\Modules`, plus the all-users WinPS root when actually running on 5.1) as well as the PowerShell 7 roots. This is intentional: a 5.1 user who needs a *manual* solution to the version-conflict problem can use these helpers to identify which module to connect to first. The supported way to do this today is to run the helpers **from a declared supported PowerShell session** while they inspect a machine's installed modules, understanding that the all-users WinPS root is only auto-seeded when the helper itself runs on 5.1 (see the §10 note on the manifest edition declaration). +- **Multi-targeting is a shipped isolation boundary.** `net8.0`, `net9.0`, and `net10.0` have separate physical payload directories even when individual files currently hash identically. The package-composition gate rejects missing or extra profile directories and verifies that optional CI provisioning tooling is absent. ## 2. The runtime model that drives every decision (read this first) @@ -44,7 +45,7 @@ They even run **different `Azure.Core` versions side-by-side** (Az `1.50`, Graph **Consequences:** 1. DLLPickle must **not** preload the Azure SDK stack. Preloading `Azure.Core` into the default ALC splits the identity of `Azure.Core.TokenRequestContext` across the module's private ALC boundary and breaks `Connect-AzAccount` with a `MissingMethodException` (shipped fix: 2.0.1). -2. As more modules self-isolate, **DLLPickle's necessary scope shrinks** on PS 7.4+. Its remaining value is the **MSAL + IdentityModel** stack, which default-ALC consumers (Exchange Online, Teams) still share. +2. As more modules self-isolate, **DLLPickle's necessary scope shrinks** on the supported ALC-capable profiles. Its remaining value is the **MSAL + IdentityModel** stack, which default-ALC consumers (Exchange Online, Teams) still share. > **Windows PowerShell 5.1 caveat — no ALC.** Everything above depends on `AssemblyLoadContext`, which exists only on .NET (Core) 5+. **Windows PowerShell 5.1 runs on .NET Framework 4.8, which has no ALC**, so modules **cannot** self-isolate there — every dependency lands in one shared load context. This is *why* the preloader does not support 5.1 (§1.2): the "modules manage the Azure SDK themselves" premise does not hold. If net48 / WinPS 5.1 support is ever re-added, the Azure SDK stack (`Azure.Core`, `Azure.Identity`, `Azure.Identity.Broker`, `System.ClientModel`) would need to be **preloaded again — conditionally, per-TFM (net48 only)** — exactly as #183 originally did before the 2.0 refactor over-generalized it. In other words: the `block` verdicts in §3 are correct **because** the runtime is ALC-capable; they are not universal. See §10 for the full re-introduction checklist. @@ -55,12 +56,12 @@ Every tracked assembly is classified into exactly one of: | Class | Meaning | What earns it | | --- | --- | --- | | **preload** | Bundled in DLLPickle; loaded early into the default ALC | Shared by ≥2 default-ALC consumers at diverging versions, **and** preloading one coherent version is observed to help without breaking a self-isolating module. | -| **block** | Never bundled | A self-isolating module owns it in a private ALC **and** preloading it is observed to break a scenario; or it is harmful to preload (OData/#174). | +| **block** | Never preloaded on the policy scope; normally excluded from the bundle | A self-isolating module owns it in a private ALC and preloading it breaks a scenario, or the host already provides it. A platform-scoped runtime dependency may remain in the single universal artifact for other operating systems, but the loader must filter it on the blocked platform. | | **ignore** | No action | No cross-module divergence, not harmful. | **Static narrows, runtime decides.** ALC ownership (from the runtime probe) flags *candidates*; the with/without runtime differential makes the *call*. This distinction is load-bearing: `Microsoft.Identity.Client.Extensions.Msal` is owned by Az's private ALC (a block *candidate*), yet preloading the MSAL/IdentityModel stack is **proven safe and beneficial** (2.0.1 four-module connect), so it is `preload`. Only the Azure SDK stack is `block`. -**Current classification (net8.0 profile):** +**Current classification (all three ALC-capable profiles):** | Assemblies | Class | Basis | | --- | --- | --- | @@ -74,10 +75,11 @@ Every tracked assembly is classified into exactly one of: | Component | Path | Responsibility | | --- | --- | --- | | Module source | `src/DLLPickle/` | The shipped PowerShell module (public/private functions, manifest). | -| Loader | `src/DLLPickle/Public/Import-DPLibrary.ps1` | Loads the bundled `bin/net8.0` DLLs into the default ALC with dependency-ordered, retrying loads. | -| Build project | `src/DLLPickle.Build/DLLPickle.csproj` | **Realizes** the policy — preload packages are direct runtime references; blocked transitives that need suppression are direct references with `ExcludeAssets="runtime"`. `packages.lock.json` pins resolved versions. | -| Dependency policy | `build/dependency-policy.json` | **Decision source of truth** — per-assembly classification + evidence, monitored modules, target scenario, drift baseline. | -| Analysis tools | `tools/Get-DLLPickleLoadedTrackedAssembly.ps1`, `New-DLLPickleConflictMatrix.ps1`, `Compare-DLLPickleConflictMatrix.ps1`, `Get-DLLPickleRuntimeAssemblySnapshot.ps1`, `Get-DLLPickleUpstreamInventory.ps1`, `Update-DLLPickleDependencyPins.ps1` | Inventory upstream modules, build the conflict matrix, probe runtime ALC ownership (filter sourced from `trackedAssemblies`), detect drift, and apply policy pins. | +| Runtime policy and loader | `src/DLLPickle/SupportedRuntimeProfiles.json`, `src/DLLPickle/Private/Get-DPRuntimeProfile.ps1`, `src/DLLPickle/Public/Import-DPLibrary.ps1` | Maps the running PowerShell/CLR pair to one exact TFM and loads that isolated bundle with dependency-ordered, retrying loads. | +| Build project | `src/DLLPickle.Build/DLLPickle.csproj` | **Realizes** the policy — preload packages are direct runtime references; blocked transitives that must never ship use `ExcludeAssets="runtime"`; platform-scoped host assemblies needed elsewhere remain in the universal payload and are filtered by `SupportedRuntimeProfiles.json`. `packages.lock.json` pins resolved versions. | +| Dependency policy | `build/dependency-policy.json` | **Decision source of truth** — per-profile and per-platform classifications, monitored modules, import orders, validation tiers, and conflict baselines. | +| Profile evidence | `build/profile-evidence/` | Durable normalized snapshots for each accepted PowerShell/TFM/OS/architecture cell. Policy baselines bind the committed snapshot path and content fingerprint. | +| Analysis tools | `tools/Get-DLLPickleLoadedTrackedAssembly.ps1`, `New-DLLPickleConflictMatrix.ps1`, `Compare-DLLPickleConflictMatrix.ps1`, `Get-DLLPickleRuntimeAssemblySnapshot.ps1`, `Get-DLLPickleUpstreamInventory.ps1`, `New-DLLPickleNormalizedProfileEvidence.ps1`, `Update-DLLPickleDependencyPins.ps1` | Inventory upstream modules, build the conflict matrix, normalize durable evidence, probe runtime ALC ownership (filter sourced from `trackedAssemblies`), detect drift, and apply policy pins. | | Build script | `build/DLLPickle.Build.ps1` | Invoke-Build tasks: Analyze, AnalyzeTests, AnalyzeTools, Test, RestoreDependencies, PrepareModuleOutput, IntegrationTest. | | Release scripts | `.github/ci-scripts/Get-VersionBump.ps1` | Decides the semantic-version bump (and whether to release at all) from Conventional Commit prefixes since the last tag. The publish-decision logic behind §8.1. | | CI | `.github/workflows/` | Build/test matrix, Upstream-Compatibility (inventory + drift), Dependabot auto-approve, path+commit-gated `Release-and-Publish`. | @@ -92,6 +94,7 @@ Every tracked assembly is classified into exactly one of: | What is actually bundled? | `src/DLLPickle.Build/DLLPickle.csproj` (+ `packages.lock.json`) — must match the policy's `preload` set | | How does the loader behave? | `src/DLLPickle/Public/Import-DPLibrary.ps1` | | Which runtime/edition is supported? | §1.2 (platform-support contract) + `src/DLLPickle/DLLPickle.psd1` | +| What are the current exact patches and lifecycle dates? | `build/powershell-test-matrix.json` + [generated support matrix](generated/Support-Matrix.md) | | When does a merged PR publish a new version? | §8.1 + `.github/workflows/Release-and-Publish.yml` + `.github/ci-scripts/Get-VersionBump.ps1` | | How is a dependency update adjudicated and shipped? | §8.2 + `build/dependency-policy.json` + `.github/workflows/Dependabot-Auto-Approve.yml` | | What maintenance traps and follow-up gaps remain? | `docs/gaps/README.md` and the individual `docs/gaps/GAP-*.md` files | @@ -102,21 +105,23 @@ Every tracked assembly is classified into exactly one of: ## 6. Invariants (and the gate that enforces each) -- **No `block` assembly appears in `module/DLLPickle/bin/net8.0`.** → policy-driven `tests/Integration/DependencyPolicyRealization.Tests.ps1` plus scenario-specific guards in `tests/Integration/DLLPickle.IntegrationTest.Tests.ps1`. +- **No `block` assembly appears in any supported TFM payload.** → policy-driven `tests/Integration/DependencyPolicyRealization.Tests.ps1` plus scenario-specific guards in `tests/Integration/DLLPickle.IntegrationTest.Tests.ps1`. - **No preloaded assembly is loaded into two ALCs at once** in the four-module scenario. → integration ALC-split guard (runtime tier; maintainer-run with real modules). - **The bundled `preload` set equals `dependency-policy.json`'s `preload` entries.** → `tests/Integration/DependencyPolicyRealization.Tests.ps1`. -- **Runtime-provided BCL assemblies are never preloaded.** Some assemblies are platform-scoped: `System.Security.Cryptography.ProtectedData` is provided by PowerShell only on Windows; on Linux/macOS it is bundled (not excluded). See `build/dependency-policy.json` for platform-specific block scopes. → policy block classification + `tests/Integration/DependencyPolicyRealization.Tests.ps1` with platform-aware filtering. +- **Runtime-provided BCL assemblies are never preloaded.** `System.Security.Cryptography.ProtectedData` is provided by PowerShell only on Windows, but the published module is one cross-platform artifact assembled on Windows. It is therefore bundled for Linux/macOS, while `SupportedRuntimeProfiles.json` makes the Windows loader skip the bundled copy. See `build/dependency-policy.json` for the platform-specific block scope. → policy realization + exact-runtime integration coverage. - **Analysis tooling is correct.** → `tests/Unit/ConflictMatrix.Tests.ps1`, `tests/Unit/ConflictMatrixDrift.Tests.ps1`. +- **Only the three declared TFM directories ship, and multi-pwsh never ships.** → `tools/Test-DLLPicklePackageArtifact.ps1` + `tests/Unit/ArtifactPolicy.Tests.ps1`. +- **Material artifact growth requires review.** → `tools/New-DLLPickleArtifactSizeReport.ps1` against `build/artifact-size-baseline.json`. A captured baseline also fails strict validation until a maintainer sets `approvalStatus` to `accepted` with an approval timestamp; capture time is not approval. - **`tests/` and `tools/` stay analyzer-clean.** → `AnalyzeTests` / `AnalyzeTools` build tasks (throw on any finding; `tests/` excludes only `PSUseDeclaredVarsMoreThanAssignments`). ## 7. Validation gates -- **Unit + analyzer:** `Invoke-Build -Task Analyze,Test` (the PR-smoke gate). Must be green. +- **Unit + analyzer:** `tools/Invoke-DLLPickleBuild.ps1 -Task Analyze,Test` imports the exact pinned tool versions in a fresh no-profile process. Must be green. - **Issue reproduction + composition:** `Invoke-Build -Task IssueReproTest` (synthetic modules; deterministic). -- **Runtime adjudication — non-auth tier (CI-capable):** strict per-module ALC snapshots (module/probe failures are fatal) + the four-module import/composition smoke. -- **Runtime adjudication — auth tier (maintainer-run; future Stage 2b automatable via GitHub OIDC + Entra federated credential):** real `Connect-*` to a dev tenant. This is the sign-off for any change to the bundled set. +- **Runtime adjudication — deterministic no-auth tier:** nine exact stock-host cells (three PowerShell/.NET/TFM profiles × Windows/Linux/macOS), strict per-module ALC snapshots, both relevant import orders, structured runtime identity, and fail-closed profile/platform baselines. +- **Runtime adjudication — authenticated read-only tier:** maintainer-approved credentials only. The exact unexecuted commands and status are generated in [Compatibility-Evidence.md](generated/Compatibility-Evidence.md); they are release gates and are never inferred from deterministic results. `Release-and-Publish` prefers a successful `Authenticated-Compatibility.yml` run for the exact reviewed candidate commit and one unexpired `authenticated-compatibility-evidence` artifact. Until that protected workflow and environment exist, a temporary manual record can authorize only release `3.0.0`, only for its exact bundle-source fingerprint, only after all fixed three-profile Windows read scenarios pass with zero writes, and for no more than 30 days. `Test-DLLPickleManualAuthenticatedEvidence.ps1` rejects any other release, bundle, profile set, skipped probe, expired record, missing maintainer acceptance, or credential-bearing evidence; absence of either valid route fails closed. - **Publish trigger:** `Release-and-Publish` publishes a merged PR only when it passes **both** the bundle-affecting **path gate** and the Conventional-Commit **version gate** — see §8.1 for the full contract (including how a Dependabot `deps:` commit maps to a **minor** release). CI-, policy-, docs-, test-, and tooling-only changes do **not** publish a new gallery version; `workflow_dispatch` is the deliberate-release escape hatch. -- **Dependency PRs (Dependabot):** the bumped *bundle* is validated by **Build Module** (full build under `--locked-mode` + the #193/Azure.Core repro guards), bounded by the csproj floating-with-cap constraints. The Upstream-Compatibility `pr-smoke` adds an upstream-latest freshness check for policy/tooling changes, and the scheduled candidate flow performs live inventory, drift detection, TFM alignment, and candidate PR generation. +- **Dependency PRs (Dependabot):** the bumped *bundle* is validated by **Build Module** (locked restore, all TFMs, exact runtime matrix, composition, size, and issue-repro guards). The Upstream-Compatibility gate records profile-aware selected assets, hashes, ALCs, import orders, deterministic probes, and conflict fingerprints. A conditional TFM pin, policy/classification edit, or material size increase routes to maintainer review. ## 8. Release & dependency-update contract @@ -149,14 +154,14 @@ A "tracked dependency" is one of the NuGet packages bundled into the preload set **Step 0 — TFM-alignment check (precondition for both paths).** Before any merge, confirm the new release aligns with the supported target framework moniker(s). "TFM-aligned" means **both**: -- **(a) Build-gate proof** — the project still restores under `--locked-mode` and builds + passes Pester/CI green on `net8.0` (the `Build gate` required check); **and** -- **(b) Explicit TFM inspection** — the package actually ships an assembly asset consumable by the supported TFM (`net8.0` today) — for example a `net8.0`, `netstandard2.0`, or `netstandard2.1` asset — rather than appearing to work only by luck of transitive resolution. Enforced by `tools/Test-DLLPickleTfmAlignment.ps1`, which inspects each preload package's `lib//` assets and runs fail-closed in the scheduled Upstream-Compatibility candidate flow. +- **(a) Build-gate proof** — the project still restores under `--locked-mode`, builds all three target frameworks, and passes Pester plus every exact runtime/OS cell (the stable `Build gate` required check); **and** +- **(b) Explicit TFM inspection** — NuGet's restored `project.assets.json` selects a managed asset for every preload package on `net8.0`, `net9.0`, and `net10.0`. Enforced by `tools/Test-DLLPickleTfmAlignment.ps1`; the report names each resolved graph and selected asset rather than approximating compatibility with a handwritten regex. A release that fails either half is not TFM-aligned and must not be merged on the automated path. **Minor / patch release** → run Step 0 (TFM alignment) + Pester + CI, then **approve and merge with a detailed PR comment** recording what moved, the alignment evidence, and the conflict-surface result. Because identity-library bumps are the module's core deliverable, a minor/patch dependency bump produces a **minor** module release: Dependabot's `deps:` commit prefix is a recognized minor release prefix (§8.1), so an auto-merged bump fires the version gate on its own. -**Major release** → still **fully tested** (Pester + CI) and **verified for architecture alignment** (Step 0, plus a re-adjudication of the §3 preload/block classifications, since a major upstream jump can move the conflict surface), **but it lands as a draft PR with fully detailed notes** — never auto-merged and never auto-published. The notes should cover: the version delta and an upstream changelog / breaking-change summary, the TFM-alignment result, the Pester/CI outcome, and the conflict-surface / `dependency-policy.json` impact. A maintainer promotes the draft to ready and merges after review; the merge then publishes a **major** module release (carried as `breaking:`). This is implemented in `Dependabot-Auto-Approve.yml` (draft conversion + the structured-notes scaffold). +**Major release** → still **fully tested** (Pester + CI) and **verified for architecture alignment** (Step 0, plus a re-adjudication of the §3 preload/block classifications), **but it lands as a draft PR with per-TFM resolved graphs, selected assets, assembly delta, conflict-surface delta, size delta, and scenario outcomes** — never auto-merged and never auto-published. A maintainer promotes the draft to ready and merges after review; the merge then publishes a **major** module release (carried as `breaking:`). This mirrors the **hard gate** in §9: bundle-set changes are behavior-changing and require the auth-tier real-environment sign-off; they are not auto-merged blindly. @@ -191,17 +196,17 @@ The path gate in §8.1 is deliberate: a merge only auto-publishes when it change When changing the preload contract, follow this loop: 1. **Inventory** the monitored modules (`Get-DLLPickleUpstreamInventory.ps1`). -2. **Build and compare the conflict matrix** (`New-DLLPickleConflictMatrix.ps1`, then `Compare-DLLPickleConflictMatrix.ps1`) to find candidates and structured new/removed conflict, version, and contributor changes. The matrix also emits the drift `Fingerprint` — a SHA-256 over each diverging assembly's name, sorted versions, **and** contributing-module set (`ShippedBy`) — which the Upstream-Compatibility gate compares to `baseline.conflictSurfaceFingerprint`. Any structured change trips drift. +2. **Build and compare the conflict matrix in every exact runtime profile** (`New-DLLPickleConflictMatrix.ps1`, then `Compare-DLLPickleConflictMatrix.ps1`) to find candidates and structured new/removed conflict, version, contributor, and ALC-owner changes. The matrix emits a profile-keyed `Fingerprint`; `Test-DLLPickleProfileConflictBaseline.ps1` compares it only with the matching PowerShell line, TFM, platform, module-set, and import-order baseline under `runtimeProfiles[].baselines`. Missing, unaccepted, or changed profile evidence fails closed. The top-level `baseline` and each `legacyUnscopedBaseline` are retained only as historical #239/#273 evidence and are not authoritative for a supported profile. 3. **Probe runtime ALC ownership** (`Get-DLLPickleRuntimeAssemblySnapshot.ps1`) — private-ALC ownership is a `block` *candidate*, not an automatic verdict. 4. **Adjudicate** with the runtime differential (does preloading help without breaking?). Record the verdict + evidence in `build/dependency-policy.json`. -5. **Realize** in `DLLPickle.csproj` (preload = bundled reference; block = excluded, incl. `ExcludeAssets` for blocked transitives), regenerate `packages.lock.json`. +5. **Realize** in `DLLPickle.csproj` (preload = bundled reference; ordinary block = excluded; a platform-scoped block needed by another OS remains bundled and is filtered by the shipped runtime policy), regenerate `packages.lock.json`. 6. **Validate** (non-auth gates always; auth tier for any bundled-set change). 7. **Update this blueprint** if the contract or invariants changed. 8. **Update the gap register** if the work opens, advances, resolves, blocks, supersedes, or intentionally accepts a tracked gap. -The baseline is a reproducible snapshot, not just a hash: `baseline.conflictSurface` stores every diverging assembly's `name`, sorted `versions`, and sorted `shippedBy` contributors alongside the module versions and fingerprint. Baseline refreshes must resolve all monitored versions before downloading any module, then prove that the stored rows recompute to the recorded fingerprint. Version-only moves are material drift and require adjudication; they do not pass silently. +Each accepted profile baseline is a reproducible snapshot, not just a hash. `New-DLLPickleNormalizedProfileEvidence.ps1` converts runner-specific paths to stable `upstream:`, `dllpickle:`, or exact-host `runtime:` asset identifiers and writes the selected module version, assembly name/version/hash/path, contributor, import order, operating system, architecture, and AssemblyLoadContext. Volatile run ID, URL, commit, timestamp, and observed OS description remain reviewable provenance but are excluded from the content fingerprint. Each `runtimeProfiles[].baselines` entry names the committed snapshot under `build/profile-evidence/` and its content fingerprint; the baseline gate recomputes that snapshot and requires current evidence to match it. Baseline refreshes resolve all monitored versions before downloading any module, then prove that the stored rows recompute to the recorded fingerprint. Version-only, contributor, selected-asset, or ALC-owner moves are material drift and require adjudication; they do not pass silently. `New-DLLPickleProfileEvidenceSummary.ps1` combines the nine comparisons into one stable finding marker so unchanged scheduled results can be deduplicated without falling back to the legacy global hash. -Issue #239 was adjudicated on 2026-06-20 against Microsoft.Graph.Authentication 2.38.0, ExchangeOnlineManagement 3.10.0, Az.Storage 9.7.0, Az.Accounts 5.5.0, and MicrosoftTeams 7.8.0. The 16 conflict names and their contributors were unchanged. Strict probes of both configured import orders, with and without DLLPickle preloading, confirmed the existing preload/block classifications, so only the structured policy baseline changed; the bundled set and public module behavior did not. +Issue #239 was adjudicated on 2026-06-20 against Microsoft.Graph.Authentication 2.38.0, ExchangeOnlineManagement 3.10.0, Az.Storage 9.7.0, Az.Accounts 5.5.0, and MicrosoftTeams 7.8.0. That snapshot remains useful historical context, but it predates the nine-profile evidence contract and cannot be carried forward as a current net8/net9/net10 or cross-platform verdict. **Hard gates (non-negotiable):** @@ -242,7 +247,7 @@ Detailed status for open and in-progress maintenance traps is tracked in the [ga | Gap | Status | Architecture note | | --- | --- | --- | | [GAP-002](gaps/GAP-002-az-resources-monitoring.md) | resolved | `Az.Resources` is now included in `monitoredModules`; policy tracking scope and dependency docs were updated with structural test coverage. | -| [GAP-003](gaps/GAP-003-exo-teams-probe-commands.md) | open | EXO/Teams ALC ownership is not yet captured because bare `Import-Module` does not eagerly load their identity assemblies. | +| [GAP-003](gaps/GAP-003-exo-teams-probe-commands.md) | in-progress | Deterministic and authenticated read-only EXO/Teams probe commands are implemented and structurally tested; exact-profile CI evidence remains unaccepted. | | [GAP-004](gaps/GAP-004-vscode-powershelleditorservices-host.md) | open | VS Code / PowerShellEditorServices host behavior is not yet modeled for issue #169. | | [GAP-005](gaps/GAP-005-odata-conflict-expectation-management.md) | resolved | OData/#174 expectation management is explicit in known-conflict data, docs, and policy tests; changes require runtime re-adjudication. | | [GAP-006](gaps/GAP-006-release-dispatch-process-trap.md) | resolved | The manual release dispatch runbook (§8.3) documents which changes auto-publish and when `workflow_dispatch` is required; guarded by `tests/Unit/WorkflowGuardrails.Tests.ps1`. | @@ -255,11 +260,11 @@ Historical/resolved notes remain below when they explain the current architectur - **Dependency bumps publish on their own — `deps → minor` (decisions 3 & 4).** *Resolved.* `Get-VersionBump.ps1` now recognizes Dependabot's NuGet `deps:` commit prefix (`.github/dependabot.yml`) as a **minor** release prefix (§8.1), so an auto-approved, squash-merged minor/patch dependency bump satisfies both the path gate and the version gate and publishes a **minor** module release. (A maintainer-promoted major dependency PR still carries `breaking:`.) Covered by `tests/Unit/GetVersionBump.Tests.ps1`. - **Major-dependency draft-PR flow.** *Resolved.* `Dependabot-Auto-Approve.yml` converts a `version-update:semver-major` PR to a **draft** (`gh pr ready --undo`) and posts structured notes (version delta, NuGet package link, TFM-alignment references, Build gate / CI links, conflict-surface / `dependency-policy.json` impact, and a maintainer checklist) instead of the former generic comment. Majors remain excluded from `gh pr merge --auto`. Guarded by `tests/Unit/WorkflowGuardrails.Tests.ps1`. -- **Explicit TFM-alignment inspection (Step 0b).** *Resolved.* `tools/Test-DLLPickleTfmAlignment.ps1` inspects each preload package's `lib//` assets (or a legacy flat `lib/`) and asserts a net8.0-consumable asset is present, including `netstandard2.0` and `netstandard2.1` where applicable, complementing the `Build gate` (Step 0a). It runs fail-closed in the scheduled Upstream-Compatibility candidate flow and is referenced from the major-dependency draft-PR notes. (`Get-DLLPickleUpstreamInventory.ps1` still captures only assembly `Name`/`Version`/`FullName`; the TFM assertion lives in the dedicated tool, which inspects the restored NuGet package layout.) Covered by `tests/Unit/TfmAlignment.Tests.ps1`. -- **Platform-support contract vs. manifest edition (decision 2).** The inspection/diagnostic tier is intended to be cross-edition (§1.2), but the manifest declares `CompatiblePSEditions = @('Core')`, so *importing* the module under Windows PowerShell 5.1 surfaces a compatibility warning. The supported manual-remediation path is therefore to run the inspection helpers **from a PowerShell 7.4+ session** while they scan the Windows PowerShell module roots — not to import DLLPickle under 5.1. Two code paths back this cross-edition intent on purpose: `Set-DPConfig` keeps a `$PSEdition`-aware encoding fallback, and `Find-DLLInPSModulePath` seeds the `WindowsPowerShell\Modules` roots (the all-users WinPS root only when actually running on 5.1). These are intentional, not residual dead code; recorded here so the contract is explicit. -- **Multi-TFM (net9.0/net10.0):** deferred; the methodology is TFM-parameterizable. net9.0/net10.0 are ALC-capable, so the `block` verdicts in §3 carry over to them. The `net8.0` bundle is confirmed to load on **PS 7.6 / .NET 10 via roll-forward** (Az.Resources import verified, no #193 regression) — a positive signal that multi-TFM is mostly a packaging exercise, not a behavioral one, on ALC-capable runtimes. When it lands, `dependency-policy.json` preload entries (currently a single `targetFramework: net8.0` each) and the `Update-DLLPickleDependencyPins` tooling will need a per-TFM representation; the `RestoreDependencies` task already parses both `TargetFramework` and `TargetFrameworks` in anticipation. +- **Explicit TFM-alignment inspection (Step 0b).** *Resolved.* `tools/Test-DLLPickleTfmAlignment.ps1` reads NuGet's restored `project.assets.json` and verifies the selected compile/runtime assets for every preload package on `net8.0`, `net9.0`, and `net10.0`, complementing the exact-runtime `Build gate` (Step 0a). Covered by `tests/Unit/TfmAlignment.Tests.ps1`. +- **Platform-support contract vs. manifest edition (decision 2).** The inspection/diagnostic tier is intended to be cross-edition (§1.2), but the manifest declares `CompatiblePSEditions = @('Core')`, so *importing* the module under Windows PowerShell 5.1 surfaces a compatibility warning. The supported manual-remediation path is therefore to run the inspection helpers **from a declared supported PowerShell session** while they scan the Windows PowerShell module roots — not to import DLLPickle under 5.1. Two code paths back this cross-edition intent on purpose: `Set-DPConfig` keeps a `$PSEdition`-aware encoding fallback, and `Find-DLLInPSModulePath` seeds the `WindowsPowerShell\Modules` roots (the all-users WinPS root only when actually running on 5.1). These are intentional, not residual dead code; recorded here so the contract is explicit. +- **Multi-TFM support:** implemented. The build emits physically isolated `net8.0`, `net9.0`, and `net10.0` payloads; the loader selects them fail-closed from the PowerShell/CLR pair; dependency decisions use `targetFrameworks`; NuGet compatibility comes from `project.assets.json`; and the pin updater preserves common versions unless reviewed evidence introduces an existing conditional reference. The exact nine-cell runtime matrix remains the acceptance authority rather than roll-forward behavior. - **Re-introducing Windows PowerShell 5.1 / net48 (no ALC) — checklist if attempted:** because net48 has no `AssemblyLoadContext`, modules cannot self-isolate and the §3 `block` verdicts for the Azure SDK stack **invert**. Re-support would require: (1) multi-targeting the build to `net48` alongside `net8.0`; (2) **conditionally preloading the Azure SDK stack** (`Azure.Core` + `Azure.Identity`/`Broker` + `System.ClientModel`) for net48 only — pinned to the highest version the WinPS-supported module set agrees on, as #183 did; (3) restoring net48-specific dependency conditions in `DLLPickle.csproj` (e.g. `Condition="'$(TargetFramework)' == 'net48'"`); (4) restoring `CompatiblePSEditions = @('Core','Desktop')` and lowering the manifest `PowerShellVersion`, plus per-edition guards in `Import-DPLibrary`; (5) adding WinPS 5.1 to the CI test matrix and re-validating the #156/#165-class scenarios. The 2.0 refactor's mistake was applying the net48-era Azure.Core preload to net8 unconditionally — any re-introduction must keep it **strictly TFM-conditional**. -- **Planned feature enhancements (backlog).** Forward-looking ideas consolidated from the former root `Roadmap.md`; not yet scheduled, order is not guaranteed, and large dependency/platform shifts may change priority. (Released and in-progress work lives in [CHANGELOG.md](../CHANGELOG.md) — for example the `Microsoft.PowerShell.PlatyPS` help-generation migration is already tracked there, and the broader PowerShell 7.4+ compatibility and supply-chain hardening work is the ongoing subject of §3, §8, and §9.) +- **Planned feature enhancements (backlog).** Forward-looking ideas consolidated from the former root `Roadmap.md`; not yet scheduled, order is not guaranteed, and large dependency/platform shifts may change priority. (Released and in-progress work lives in [CHANGELOG.md](../CHANGELOG.md) — for example the `Microsoft.PowerShell.PlatyPS` help-generation migration is already tracked there, and the broader supported-profile compatibility and supply-chain hardening work is the ongoing subject of §3, §8, and §9.) - Import a specific version of MSAL (`Microsoft.Identity.Client`) on demand, rather than only the bundled pin. - Verify a package's hash/signature against the original source (e.g. NuGet) metadata before preload — a supply-chain integrity check extending the dependency-update work in §8. - Option to preload additional common (non-identity) assemblies beyond the default identity stack. diff --git a/docs/DEPENDENCIES.md b/docs/DEPENDENCIES.md index f1237bcd..733cb73e 100644 --- a/docs/DEPENDENCIES.md +++ b/docs/DEPENDENCIES.md @@ -5,16 +5,19 @@ update policies. ## Runtime Baseline -DLLPickle now targets **PowerShell 7.4+** with a single **net8.0** runtime -profile. Legacy Windows PowerShell 5.1 and .NET Framework dependency paths are -no longer supported. +DLLPickle ships three physically isolated bundles for the exact profiles in the +[generated support matrix](generated/Support-Matrix.md): PowerShell 7.4 / .NET 8 +(`net8.0`), PowerShell 7.5 / .NET 9 (`net9.0`), and PowerShell 7.6 / .NET 10 +(`net10.0`). The loader checks both the PowerShell minor and CLR major and fails +closed on an undeclared or mismatched profile. Legacy Windows PowerShell 5.1 and +.NET Framework dependency paths are not supported by the automated preloader. The *automated* fix (`Import-DPLibrary` / `Import-DPBaseProfile`) requires -PowerShell 7.4+ / .NET 8 because it depends on `AssemblyLoadContext`. The +one of those supported .NET profiles because it depends on `AssemblyLoadContext`. The *inspection / diagnostic* helpers (`Find-DLLInPSModulePath`, `Get-ModuleImportCandidate`, `Get-ModulesWithDependency`, `Get-ModulesWithVersionSortedIdentityClient`, `Test-DPLibraryConflict`) are -intentionally cross-edition — from a PowerShell 7.4+ session they still inspect +intentionally cross-edition — from a supported PowerShell session they still inspect the current-user Windows PowerShell roots (for example `Documents\WindowsPowerShell\Modules`), and when actually running on 5.1 they also auto-seed the all-users WinPS root. That lets a Windows PowerShell 5.1 @@ -35,16 +38,16 @@ For usage guidance, see [README.md](../README.md) and [docs/index.md](index.md). ## Dependency Management Strategy Every tracked-dependency release is first checked for **target-framework -alignment**: it must restore, build, and pass tests on `net8.0` under -`--locked-mode`, and ship a net8.0-consumable assembly asset (for example -`net8.0`, `netstandard2.0`, or `netstandard2.1`, as verified by -`tools/Test-DLLPickleTfmAlignment.ps1`). Only then does the severity of the -version jump decide how it ships: +alignment**: it must restore under `--locked-mode`, build and pass tests for all +three TFMs, and have NuGet select a managed asset for every preload package in +each restored `project.assets.json` target graph. `tools/Test-DLLPickleTfmAlignment.ps1` +records those selected assets rather than approximating compatibility from folder +names. Only then does the severity of the version jump decide how it ships: | Update Type | Policy | | ----------- | ------ | -| **Patch / Minor** (x.Y.Z) | After the TFM-alignment + test gate, approve and merge with a detailed PR comment. Identity-library bumps are the module's core deliverable, so they ship as a **minor** module release — Dependabot's `deps:` commit is a recognized minor release prefix, so an auto-merged bump publishes a minor release on its own. | -| **Major** (X.y.z) | Still fully tested and TFM-verified, with the conflict surface re-adjudicated, but opened as a **draft PR with fully detailed notes** — **not** auto-merged and **not** auto-published. A maintainer promotes and merges it, publishing a **major** release (`breaking:`). | +| **Patch / Minor** (x.Y.Z) | May auto-approve and register auto-merge only when the exact actor/author, file-set, complete TFM/runtime matrix, upstream-policy, build, artifact-size, and dependency-review gates pass. A conditional TFM pin, classification edit, or material size growth routes to review. Identity-library bumps ship as a **minor** module release through the `deps:` prefix. | +| **Major** (X.y.z) | Fully tested and TFM-verified, with per-TFM graph/asset/assembly/size evidence and the conflict surface re-adjudicated, but converted to a **draft PR** — **not** auto-merged and **not** auto-published. A maintainer promotes and merges it as a **major** release (`breaking:`). | | **Upstream PowerShell module drift** | Candidate PR or issue after the scheduled inventory + drift check. | > **Publish note.** A merged dependency PR publishes a new gallery version only @@ -61,8 +64,9 @@ version jump decide how it ships: The automation that supports this: Dependabot opens NuGet update PRs; the **Dependabot-Auto-Approve** workflow auto-approves and squash-merges patch/minor updates (restricted to the exact `DLLPickle.csproj` / `packages.lock.json` -allow-list, and only after the `Build gate`, `Validate upstream compatibility -tooling`, and `dependency-review` required checks pass) and excludes major + allow-list, and only after the `Build gate`, `Validate upstream compatibility + tooling`, complete exact-runtime matrix, artifact-size policy, and + `dependency-review` required checks pass) and excludes major updates from auto-merge, converting them to a reviewed **draft PR** with detailed notes instead. @@ -99,7 +103,10 @@ docs-, policy-, and tooling-only changes do not trigger a release. See Dependabot tracks NuGet package releases, but DLLPickle also tracks the DLLs bundled by upstream PowerShell modules. The scheduled **Upstream Compatibility** workflow uses `build/dependency-policy.json` and tools under `tools/` to -inventory latest PSGallery releases and propose safe candidate pin updates. +inventory the newest compatible PSGallery release independently in each exact +PowerShell profile. It records the umbrella and constituent module, selected +asset path, hash, ALC, OS, architecture, import order, and deterministic probe. +The generated status is [Compatibility Evidence](generated/Compatibility-Evidence.md). Monitored modules: @@ -119,7 +126,9 @@ candidate generation, restore, build, and issue reproduction tests pass. ## NuGet Package Dependencies Version strategy in `DLLPickle.csproj` is **major-locked floating** (`N.*`); the -lock file pins the concrete resolved version. +lock file pins the concrete resolved version for all three TFMs. Common versions +are preserved across TFMs unless profile evidence requires a reviewed conditional +pin. | Package | Version Strategy | Notes | | ------- | ---------------- | ----- | @@ -157,16 +166,15 @@ their own code owners: sessions can fail when one module binds to a lower, incompatible assembly. - Candidate pin updates are generated from upstream module inventories and still require full validation before publication. -- `Azure.Core` is intentionally **not** preloaded on the PowerShell 7.4+ - (net8.0) profile. Az.Accounts 5.x isolates its Azure SDK stack in a private +- `Azure.Core` is intentionally **not** preloaded on any supported ALC-capable + profile. Az.Accounts 5.x isolates its Azure SDK stack in a private `AssemblyLoadContext`; preloading `Azure.Core` into the default load context splits the identity of `Azure.Core.TokenRequestContext` across load contexts and breaks `Connect-AzAccount` with a `MissingMethodException` on `InteractiveBrowserCredential.AuthenticateAsync`. Graph, Exchange, and Teams - resolve a compatible `Azure.Core` themselves on .NET 8, so the preload is + resolve a compatible `Azure.Core` themselves, so the preload is unnecessary. `Azure.Core` remains report-only in policy for monitoring. The - original net48-only `Azure.Core` preload (#183) does not apply to the net8.0 - baseline. + original net48-only `Azure.Core` preload (#183) does not apply to these profiles. - OData families remain report-only in policy because preloading them by default can break compatibility when upstream modules require different OData identities. @@ -206,6 +214,8 @@ Manual review required for: - Major version upgrades - New package additions - Changes to version strategy +- A new conditional per-TFM package pin or preload/block classification change +- A material breach of `build/artifact-size-baseline.json` ## References diff --git a/docs/Deep-Dive.md b/docs/Deep-Dive.md index 7aaa02a6..fa644921 100644 --- a/docs/Deep-Dive.md +++ b/docs/Deep-Dive.md @@ -32,10 +32,16 @@ PowerShell modules: ## How DLLPickle Works -`Import-DPLibrary` loads DLLs from the module's packaged `bin` folder that -matches the supported runtime target: +`Import-DPLibrary` selects a physically isolated DLL directory using both the +PowerShell minor and CLR major: -- `bin/net8.0` for PowerShell 7.4+ +- `bin/net8.0` for PowerShell 7.4 / .NET 8 +- `bin/net9.0` for PowerShell 7.5 / .NET 9 +- `bin/net10.0` for PowerShell 7.6 / .NET 10 + +The [generated support matrix](generated/Support-Matrix.md) supplies the current +Microsoft lifecycle and exact CI patch evidence. An unknown or mismatched pair +fails closed rather than rolling forward to another bundle. To improve reliability, the loader: @@ -52,7 +58,7 @@ predictable and diagnosable. ## The inspection helpers (and Windows PowerShell 5.1) -`Import-DPLibrary` is the *automated* fix, and it needs PowerShell 7.4+ / .NET 8 +`Import-DPLibrary` is the *automated* fix, and it needs a declared supported PowerShell/.NET profile because it depends on `AssemblyLoadContext`. But DLLPickle also ships a set of **inspection helpers** that are deliberately cross-edition: @@ -69,7 +75,7 @@ because it depends on `AssemblyLoadContext`. But DLLPickle also ships a set of These exist so the project charter still helps environments the preloader cannot reach. A **Windows PowerShell 5.1** user who hits the same DLL conflict can run -these helpers from a PowerShell 7.4+ session, inspect the current-user Windows +these helpers from a supported PowerShell session, inspect the current-user Windows PowerShell module roots from there, see which installed module ships the newest identity DLL, and connect to that service *first* — the same "first one wins" idea, applied by hand. The automated preload is the convenience; the manual @@ -89,7 +95,8 @@ workflow: - `build/dependency-policy.json` declares monitored PSGallery modules, tracked assembly families, per-assembly version policies, and blocked preload families. - `tools/Get-DLLPickleUpstreamInventory.ps1` downloads and inventories the - latest monitored modules. + newest release compatible with the exact tested PowerShell profile, then + records only the assets actually selected in that stock host. - `tools/Update-DLLPickleDependencyPins.ps1` compares the inventory with the policy and applies safe candidate pin updates (major-locked floating `N.*`, or an exact pin when a `maximumPackageVersion` cap applies). - `.github/workflows/Upstream-Compatibility.yml` runs the inventory and @@ -107,10 +114,11 @@ The monitored module set currently includes: `Az.Resources` is monitored explicitly because it is the observed trigger for the #193 `Microsoft.Extensions.*` collision family. -The workflow is fail-closed. It may open a candidate PR when a policy-supported -pin changes, such as a Graph or Teams `Azure.Core` update. It does not merge or -publish changed preload behavior unless the -candidate passes restore, build, and issue reproduction validation. +The workflow is fail-closed. It may propose a candidate when a policy-supported +identity pin changes. It does not merge or publish changed preload behavior unless +the candidate passes locked restore, all-TFM build, the exact nine-cell runtime +matrix, profile/platform conflict baselines, artifact composition, size, and issue +reproduction validation. Some dependency families are deliberately report-only. For example, OData assemblies are tracked because ExchangeOnlineManagement and Az.Storage can @@ -118,13 +126,13 @@ require incompatible versions in one process, but OData is not added to the default preload set unless a future isolation strategy makes that safe. `Azure.Core` is also report-only. It is intentionally not preloaded on the -PowerShell 7.4+ profile: both `Az.Accounts` (`AzSharedAssemblyLoadContext`) and +supported ALC-capable profiles: both `Az.Accounts` (`AzSharedAssemblyLoadContext`) and `Microsoft.Graph.Authentication` (`msgraph-load-context`) isolate their Azure SDK stack in private `AssemblyLoadContext`s — they even run different `Azure.Core` versions side-by-side without conflict. Preloading `Azure.Core` into the default load context splits the identity of `Azure.Core.TokenRequestContext` across that boundary and breaks `Connect-AzAccount`. Because the modules self-manage it, the -preload is unnecessary on .NET 8. +preload is unnecessary on the supported .NET runtimes. > **Windows PowerShell 5.1 caveat:** this module self-isolation relies on > `AssemblyLoadContext`, which only exists on .NET (Core) 5+. Windows PowerShell @@ -155,11 +163,13 @@ only prepares the process and imports modules so connection commands such as `Connect-AzAccount` can run afterward using credentials and tenant choices from the caller's environment. -Live testing confirms the full base profile can connect to Exchange Online, -Microsoft Teams, Microsoft Graph, and Az.Accounts in one session. Because -DLLPickle no longer preloads `Azure.Core` on the net8.0 profile, Az.Accounts' -private `AssemblyLoadContext` resolves a single, consistent `Azure.Core`, and -`Connect-AzAccount` succeeds alongside the Graph/Exchange/Teams identity stack. +Historical authenticated testing confirmed the full base profile on the earlier +`net8.0` bundle. Current deterministic CI covers every declared PowerShell/OS +profile, but authenticated read-only checks remain explicit release gates until +approved credentials produce fresh artifacts. See +[Compatibility Evidence](generated/Compatibility-Evidence.md) for the exact +unexecuted commands. DLLPickle does not preload `Azure.Core`, so Az.Accounts' +private `AssemblyLoadContext` can resolve a consistent copy. ## Why This Helps diff --git a/docs/Troubleshooting.md b/docs/Troubleshooting.md index 0ea75c6a..a3eb5c77 100644 --- a/docs/Troubleshooting.md +++ b/docs/Troubleshooting.md @@ -20,13 +20,15 @@ credentials, VS Code, Azure Automation, or PSGallery module installation. ## Supported runtime (and Windows PowerShell 5.1) -The automated fix — `Import-DPLibrary` and `Import-DPBaseProfile` — requires -**PowerShell 7.4+ on .NET 8**. It relies on `AssemblyLoadContext`, which does not -exist on .NET Framework 4.8, so it does not run on **Windows PowerShell 5.1**. +The automated fix — `Import-DPLibrary` and `Import-DPBaseProfile` — requires one +of the exact PowerShell/.NET pairs in the +[generated support matrix](generated/Support-Matrix.md). It relies on +`AssemblyLoadContext`, which does not exist on .NET Framework 4.8, so it does not +run on **Windows PowerShell 5.1**. If you are on Windows PowerShell 5.1 and hitting the same conflict, you can still use DLLPickle's **inspection helpers** to solve it manually. Run them from a -PowerShell 7.4+ session — they still inspect the current-user Windows PowerShell +supported PowerShell session — they still inspect the current-user Windows PowerShell module roots from there — to find which installed module ships the newest identity DLL, then connect to that service *first* (the "first one wins" workaround). For example: @@ -91,17 +93,28 @@ differences by name, version, and location. ## Common loader errors -### `Binary directory not found for target framework 'net8.0'` +### `Unsupported PowerShell runtime` or `CLR mismatch` -`Import-DPLibrary` loads the bundled assemblies from the module's `bin/net8.0` -folder and throws this error if that folder is missing: +DLLPickle does not use runtime roll-forward as support evidence. Confirm +`$PSVersionTable.PSVersion`, `[Environment]::Version`, `$PSHOME`, and +`[Environment]::ProcessPath` against the [generated support matrix](generated/Support-Matrix.md). +An undeclared PowerShell line or a PowerShell/CLR pair that does not match the shipped +policy is rejected before any bundled assembly loads. Install a declared stock +PowerShell runtime or update DLLPickle after a new support-contract release; do not +rename a shim or copy another TFM directory to bypass the check. + +### `Binary directory not found for target framework ''` + +`Import-DPLibrary` maps the running PowerShell minor and CLR major to `net8.0`, +`net9.0`, or `net10.0`, then loads the bundled assemblies from that directory. +It throws this error if the selected folder is missing: ```text -Binary directory not found for target framework 'net8.0' at: \bin\net8.0 +Binary directory not found for target framework '' at: \bin\ ``` This usually means the installed module is incomplete, or you are importing from a -source tree that has not been built (the `bin/net8.0` output is generated, not +source tree that has not been built (the `bin/` output is generated, not committed). Re-install the module from the PowerShell Gallery: ```powershell @@ -124,9 +137,9 @@ and friends) for the supported base profile. The issue repro tests assert that the protected import path keeps the broker/MSAL line aligned to avoid `WithBroker` missing-method failures across mixed imports. -`Azure.Core` is intentionally not preloaded on the PowerShell 7.4+ (net8.0) +`Azure.Core` is intentionally not preloaded on any supported ALC-capable profile. The original `Azure.Core` preload (#183) was scoped to Windows -PowerShell (net48), which 2.0 no longer supports. On .NET 8, Graph, Exchange, +PowerShell (net48), which 2.0 no longer supports. On the supported .NET runtimes, Graph, Exchange, and Teams resolve a compatible `Azure.Core` themselves, and preloading it breaks `Connect-AzAccount` (see the Az.Accounts note below). @@ -162,7 +175,7 @@ in the default context splits the identity of `Azure.Core.TokenRequestContext` across the two load contexts, so Az's `InteractiveBrowserCredential` method signature no longer matches its caller. -DLLPickle no longer preloads `Azure.Core` on the net8.0 profile, so Az.Accounts +DLLPickle does not preload `Azure.Core` on any supported profile, so Az.Accounts resolves a single, consistent `Azure.Core` and `Connect-AzAccount` succeeds alongside the Graph/Exchange/Teams stack. If you still see this error, confirm no other module or profile script preloaded `Azure.Core` into the session, or diff --git a/docs/gaps/GAP-001-dependency-policy-realization-guard.md b/docs/gaps/GAP-001-dependency-policy-realization-guard.md index 723f6d5b..8dfeb65b 100644 --- a/docs/gaps/GAP-001-dependency-policy-realization-guard.md +++ b/docs/gaps/GAP-001-dependency-policy-realization-guard.md @@ -50,9 +50,9 @@ The repository has an automated integration test that compares the policy, proje - [x] A test exists that asserts every `preload` package is represented in `DLLPickle.csproj`. - [x] A test exists that asserts preload package references do not exclude runtime assets. -- [x] A test exists that asserts blocked package references exclude runtime assets when they are present in `DLLPickle.csproj`. +- [x] A test exists that asserts ordinary blocked package references exclude runtime assets, while explicitly universal platform-scoped dependencies retain theirs. - [x] A test exists that asserts every preload assembly appears in the built `bin/net8.0` output. -- [x] A test exists that asserts blocked assemblies do not appear in the built `bin/net8.0` output. +- [x] A test exists that asserts ordinary blocked assemblies do not appear in built TFM outputs and that universal platform-scoped dependencies are bundled but skipped on their host-provided platform. - [x] A test exists that asserts the built `bin/net8.0` output does not contain unclassified managed assemblies. - [x] PR #257 has merged. - [x] The gap frontmatter is updated to `status: resolved` after merge. diff --git a/docs/gaps/GAP-003-exo-teams-probe-commands.md b/docs/gaps/GAP-003-exo-teams-probe-commands.md index 01d31168..be49ae2d 100644 --- a/docs/gaps/GAP-003-exo-teams-probe-commands.md +++ b/docs/gaps/GAP-003-exo-teams-probe-commands.md @@ -1,19 +1,21 @@ --- id: GAP-003 title: Add representative EXO and Teams probe commands -status: open +status: in-progress severity: high area: runtime-probes owner: maintainer created: 2026-06-23 -updated: 2026-06-23 +updated: 2026-08-08 related_issues: [] related_prs: [] related_docs: - docs/Architecture.md - build/dependency-policy.json - docs/DEPENDENCIES.md -related_tests: [] +related_tests: + - tests/Unit/DependencyPolicy.Tests.ps1 + - tests/Unit/UpstreamInventoryProfile.Tests.ps1 resolution_pr: resolved_on: --- @@ -22,7 +24,7 @@ resolved_on: ## Status -**Current status:** Open. +**Current status:** In progress. The probe-command contract and tooling are implemented; exact profile/platform evidence has not yet been accepted. ## Problem @@ -34,8 +36,10 @@ DLLPickle's preload/block classification depends on observed runtime ownership, ## Current evidence -- `docs/Architecture.md` says static narrows but runtime decides. -- `docs/Architecture.md` records that EXO/Teams ALC ownership is not yet captured because bare `Import-Module` does not eagerly load their identity assemblies. +- `build/dependency-policy.json` assigns `Get-ConnectionInformation -ErrorAction SilentlyContinue | Out-Null` and a read-only `Get-Team` execution with errors suppressed to the deterministic no-auth tier. The Teams command executes the cmdlet surface without claiming an authenticated tenant read. +- It separately records `Get-EXOMailbox -ResultSize 1 | Out-Null` and an access-token-based `Connect-MicrosoftTeams` / `Get-CsTenant` / `Disconnect-MicrosoftTeams` sequence as authenticated read-only release gates. +- `Get-DLLPickleUpstreamInventory.ps1` passes the profile-specific deterministic command into the exact stock-host snapshot process. +- Unit tests validate policy parsing, probe separation, exact-host inventory, and selected-asset evidence without service authentication. ## Desired end state @@ -43,13 +47,13 @@ The runtime probe system supports representative `-ProbeCommand` execution for E ## Acceptance criteria -- [ ] Define safe, representative probe commands for ExchangeOnlineManagement and MicrosoftTeams. -- [ ] Update the relevant runtime probe tooling to support module-specific probe commands if it does not already. -- [ ] Record the selected probe commands in `build/dependency-policy.json` or another authoritative policy/configuration file. -- [ ] Add tests for probe-command configuration parsing and invocation behavior without requiring live authentication. -- [ ] Document which probes are CI-capable and which require maintainer-run/auth-tier validation. -- [ ] Update `docs/Architecture.md` §7, §9, or §10 as needed. -- [ ] Update `docs/gaps/README.md` and this file when resolved or superseded. +- [x] Define safe, representative probe commands for ExchangeOnlineManagement and MicrosoftTeams. +- [x] Update the relevant runtime probe tooling to support module-specific probe commands if it does not already. +- [x] Record the selected probe commands in `build/dependency-policy.json` or another authoritative policy/configuration file. +- [x] Add tests for probe-command configuration parsing and invocation behavior without requiring live authentication. +- [x] Document which probes are CI-capable and which require maintainer-run/auth-tier validation. +- [x] Update `docs/Architecture.md` §7, §9, or §10 as needed. +- [ ] Accept fresh exact-profile evidence for all required platforms and then update `docs/gaps/README.md` and this file as resolved. ## Implementation notes for Codex @@ -61,4 +65,4 @@ The runtime probe system supports representative `-ProbeCommand` execution for E ## Resolution notes -Pending. +Implementation is present in the current change. Resolution remains pending until the nine profile/platform baselines contain reviewed fingerprints rather than `requires-profile-refresh` placeholders. diff --git a/docs/gaps/README.md b/docs/gaps/README.md index aef4ed4e..236f2c4c 100644 --- a/docs/gaps/README.md +++ b/docs/gaps/README.md @@ -36,7 +36,7 @@ Use this register for durable repo-local gap state. Use `docs/superpowers/specs/ | --- | --- | --- | --- | --- | | GAP-001 | resolved | dependency-policy | Add dependency policy realization guard | [GAP-001](GAP-001-dependency-policy-realization-guard.md) | | GAP-002 | resolved | dependency-policy | Track Az.Resources as a monitored collision source | [GAP-002](GAP-002-az-resources-monitoring.md) | -| GAP-003 | open | runtime-probes | Add representative EXO and Teams probe commands | [GAP-003](GAP-003-exo-teams-probe-commands.md) | +| GAP-003 | in-progress | runtime-probes | Add representative EXO and Teams probe commands | [GAP-003](GAP-003-exo-teams-probe-commands.md) | | GAP-004 | open | host-context | Model VS Code and PowerShellEditorServices host behavior | [GAP-004](GAP-004-vscode-powershelleditorservices-host.md) | | GAP-005 | resolved | known-conflicts | Strengthen OData conflict expectation management | [GAP-005](GAP-005-odata-conflict-expectation-management.md) | | GAP-006 | resolved | release-process | Document manual release dispatch process trap | [GAP-006](GAP-006-release-dispatch-process-trap.md) | diff --git a/docs/generated/Compatibility-Evidence.md b/docs/generated/Compatibility-Evidence.md new file mode 100644 index 00000000..219ce0a9 --- /dev/null +++ b/docs/generated/Compatibility-Evidence.md @@ -0,0 +1,88 @@ +# Upstream compatibility evidence + + + +Microsoft runtime support and upstream module behavior are tracked independently. The rows below deliberately remain release-gating gaps until a profile-specific CI run is accepted; a supported PowerShell line does not imply that every upstream module combination can coexist in one process. + +| Module | Version | PowerShell | TFM | OS | Selected asset | Assembly / ALC result | Verdict | Evidence date | Run ID | +|---|---|---:|---:|---|---|---|---|---:|---| +| Microsoft.Graph.Authentication | 2.39.0 | 7.4 | `net8.0` | windows | `upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll`
`upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll` | `Azure.Core` 1.51.1.0 / `msgraph-load-context`
`System.ClientModel` 1.9.0.0 / `msgraph-load-context` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| ExchangeOnlineManagement | 3.10.1 | 7.4 | `net8.0` | windows | no tracked assembly selected | no tracked assembly observed | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Storage | 9.7.2 | 7.4 | `net8.0` | windows | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll`
`upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll`
`upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll`
`upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll`
`runtime:System.Security.Cryptography.ProtectedData.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.OData.Core` 7.6.4.0 / `Default`
`Microsoft.OData.Edm` 7.6.4.0 / `Default`
`Microsoft.Spatial` 7.6.4.0 / `Default`
`System.ClientModel` 1.13.0.0 / `AzSharedAssemblyLoadContext`
`System.Security.Cryptography.ProtectedData` 8.0.0.0 / `Default` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Accounts | 5.5.2 | 7.4 | `net8.0` | windows | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll`
`runtime:System.Security.Cryptography.ProtectedData.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext`
`System.Security.Cryptography.ProtectedData` 8.0.0.0 / `Default` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| MicrosoftTeams | 7.9.0 | 7.4 | `net8.0` | windows | `upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll` | `Microsoft.Identity.Client` 4.82.0.0 / `Default` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Resources | 10.1.0 | 7.4 | `net8.0` | windows | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Resources/10.1.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll`
`runtime:System.Security.Cryptography.ProtectedData.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Extensions.DependencyInjection.Abstractions` 10.0.0.0 / `Default`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext`
`System.Security.Cryptography.ProtectedData` 8.0.0.0 / `Default` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Microsoft.Graph.Authentication | 2.39.0 | 7.4 | `net8.0` | linux | `upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll`
`upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll` | `Azure.Core` 1.51.1.0 / `msgraph-load-context`
`System.ClientModel` 1.9.0.0 / `msgraph-load-context` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| ExchangeOnlineManagement | 3.10.1 | 7.4 | `net8.0` | linux | no tracked assembly selected | no tracked assembly observed | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Storage | 9.7.2 | 7.4 | `net8.0` | linux | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll`
`upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll`
`upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll`
`upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.OData.Core` 7.6.4.0 / `Default`
`Microsoft.OData.Edm` 7.6.4.0 / `Default`
`Microsoft.Spatial` 7.6.4.0 / `Default`
`System.ClientModel` 1.13.0.0 / `AzSharedAssemblyLoadContext` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Accounts | 5.5.2 | 7.4 | `net8.0` | linux | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| MicrosoftTeams | 7.9.0 | 7.4 | `net8.0` | linux | `upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll` | `Microsoft.Identity.Client` 4.82.0.0 / `Default` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Resources | 10.1.0 | 7.4 | `net8.0` | linux | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Resources/10.1.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Extensions.DependencyInjection.Abstractions` 10.0.0.0 / `Default`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Microsoft.Graph.Authentication | 2.39.0 | 7.4 | `net8.0` | macos | `upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll`
`upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll` | `Azure.Core` 1.51.1.0 / `msgraph-load-context`
`System.ClientModel` 1.9.0.0 / `msgraph-load-context` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| ExchangeOnlineManagement | 3.10.1 | 7.4 | `net8.0` | macos | no tracked assembly selected | no tracked assembly observed | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Storage | 9.7.2 | 7.4 | `net8.0` | macos | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll`
`upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll`
`upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll`
`upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.OData.Core` 7.6.4.0 / `Default`
`Microsoft.OData.Edm` 7.6.4.0 / `Default`
`Microsoft.Spatial` 7.6.4.0 / `Default`
`System.ClientModel` 1.13.0.0 / `AzSharedAssemblyLoadContext` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Accounts | 5.5.2 | 7.4 | `net8.0` | macos | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| MicrosoftTeams | 7.9.0 | 7.4 | `net8.0` | macos | `upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll` | `Microsoft.Identity.Client` 4.82.0.0 / `Default` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Resources | 10.1.0 | 7.4 | `net8.0` | macos | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Resources/10.1.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Extensions.DependencyInjection.Abstractions` 10.0.0.0 / `Default`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Microsoft.Graph.Authentication | 2.39.0 | 7.5 | `net9.0` | windows | `upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll`
`upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll` | `Azure.Core` 1.51.1.0 / `msgraph-load-context`
`System.ClientModel` 1.9.0.0 / `msgraph-load-context` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| ExchangeOnlineManagement | 3.10.1 | 7.5 | `net9.0` | windows | no tracked assembly selected | no tracked assembly observed | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Storage | 9.7.2 | 7.5 | `net9.0` | windows | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll`
`upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll`
`upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll`
`upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll`
`runtime:System.Security.Cryptography.ProtectedData.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.OData.Core` 7.6.4.0 / `Default`
`Microsoft.OData.Edm` 7.6.4.0 / `Default`
`Microsoft.Spatial` 7.6.4.0 / `Default`
`System.ClientModel` 1.13.0.0 / `AzSharedAssemblyLoadContext`
`System.Security.Cryptography.ProtectedData` 9.0.0.0 / `Default` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Accounts | 5.5.2 | 7.5 | `net9.0` | windows | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll`
`runtime:System.Security.Cryptography.ProtectedData.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext`
`System.Security.Cryptography.ProtectedData` 9.0.0.0 / `Default` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| MicrosoftTeams | 7.9.0 | 7.5 | `net9.0` | windows | `upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll` | `Microsoft.Identity.Client` 4.82.0.0 / `Default` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Resources | 10.1.0 | 7.5 | `net9.0` | windows | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Resources/10.1.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll`
`runtime:System.Security.Cryptography.ProtectedData.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Extensions.DependencyInjection.Abstractions` 10.0.0.0 / `Default`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext`
`System.Security.Cryptography.ProtectedData` 9.0.0.0 / `Default` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Microsoft.Graph.Authentication | 2.39.0 | 7.5 | `net9.0` | linux | `upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll`
`upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll` | `Azure.Core` 1.51.1.0 / `msgraph-load-context`
`System.ClientModel` 1.9.0.0 / `msgraph-load-context` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| ExchangeOnlineManagement | 3.10.1 | 7.5 | `net9.0` | linux | no tracked assembly selected | no tracked assembly observed | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Storage | 9.7.2 | 7.5 | `net9.0` | linux | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll`
`upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll`
`upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll`
`upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.OData.Core` 7.6.4.0 / `Default`
`Microsoft.OData.Edm` 7.6.4.0 / `Default`
`Microsoft.Spatial` 7.6.4.0 / `Default`
`System.ClientModel` 1.13.0.0 / `AzSharedAssemblyLoadContext` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Accounts | 5.5.2 | 7.5 | `net9.0` | linux | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| MicrosoftTeams | 7.9.0 | 7.5 | `net9.0` | linux | `upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll` | `Microsoft.Identity.Client` 4.82.0.0 / `Default` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Resources | 10.1.0 | 7.5 | `net9.0` | linux | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Resources/10.1.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Extensions.DependencyInjection.Abstractions` 10.0.0.0 / `Default`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Microsoft.Graph.Authentication | 2.39.0 | 7.5 | `net9.0` | macos | `upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll`
`upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll` | `Azure.Core` 1.51.1.0 / `msgraph-load-context`
`System.ClientModel` 1.9.0.0 / `msgraph-load-context` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| ExchangeOnlineManagement | 3.10.1 | 7.5 | `net9.0` | macos | no tracked assembly selected | no tracked assembly observed | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Storage | 9.7.2 | 7.5 | `net9.0` | macos | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll`
`upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll`
`upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll`
`upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.OData.Core` 7.6.4.0 / `Default`
`Microsoft.OData.Edm` 7.6.4.0 / `Default`
`Microsoft.Spatial` 7.6.4.0 / `Default`
`System.ClientModel` 1.13.0.0 / `AzSharedAssemblyLoadContext` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Accounts | 5.5.2 | 7.5 | `net9.0` | macos | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| MicrosoftTeams | 7.9.0 | 7.5 | `net9.0` | macos | `upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll` | `Microsoft.Identity.Client` 4.82.0.0 / `Default` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Resources | 10.1.0 | 7.5 | `net9.0` | macos | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Resources/10.1.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Extensions.DependencyInjection.Abstractions` 10.0.0.0 / `Default`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Microsoft.Graph.Authentication | 2.39.0 | 7.6 | `net10.0` | windows | `upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll`
`upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll` | `Azure.Core` 1.51.1.0 / `msgraph-load-context`
`System.ClientModel` 1.9.0.0 / `msgraph-load-context` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| ExchangeOnlineManagement | 3.10.1 | 7.6 | `net10.0` | windows | no tracked assembly selected | no tracked assembly observed | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Storage | 9.7.2 | 7.6 | `net10.0` | windows | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll`
`upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll`
`upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll`
`upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll`
`runtime:System.Security.Cryptography.ProtectedData.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.OData.Core` 7.6.4.0 / `Default`
`Microsoft.OData.Edm` 7.6.4.0 / `Default`
`Microsoft.Spatial` 7.6.4.0 / `Default`
`System.ClientModel` 1.13.0.0 / `AzSharedAssemblyLoadContext`
`System.Security.Cryptography.ProtectedData` 10.0.0.0 / `Default` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Accounts | 5.5.2 | 7.6 | `net10.0` | windows | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll`
`runtime:System.Security.Cryptography.ProtectedData.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext`
`System.Security.Cryptography.ProtectedData` 10.0.0.0 / `Default` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| MicrosoftTeams | 7.9.0 | 7.6 | `net10.0` | windows | `upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll` | `Microsoft.Identity.Client` 4.82.0.0 / `Default` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Resources | 10.1.0 | 7.6 | `net10.0` | windows | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Resources/10.1.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll`
`runtime:System.Security.Cryptography.ProtectedData.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Extensions.DependencyInjection.Abstractions` 10.0.0.0 / `Default`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext`
`System.Security.Cryptography.ProtectedData` 10.0.0.0 / `Default` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Microsoft.Graph.Authentication | 2.39.0 | 7.6 | `net10.0` | linux | `upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll`
`upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll` | `Azure.Core` 1.51.1.0 / `msgraph-load-context`
`System.ClientModel` 1.9.0.0 / `msgraph-load-context` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| ExchangeOnlineManagement | 3.10.1 | 7.6 | `net10.0` | linux | no tracked assembly selected | no tracked assembly observed | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Storage | 9.7.2 | 7.6 | `net10.0` | linux | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll`
`upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll`
`upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll`
`upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.OData.Core` 7.6.4.0 / `Default`
`Microsoft.OData.Edm` 7.6.4.0 / `Default`
`Microsoft.Spatial` 7.6.4.0 / `Default`
`System.ClientModel` 1.13.0.0 / `AzSharedAssemblyLoadContext` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Accounts | 5.5.2 | 7.6 | `net10.0` | linux | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| MicrosoftTeams | 7.9.0 | 7.6 | `net10.0` | linux | `upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll` | `Microsoft.Identity.Client` 4.82.0.0 / `Default` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Resources | 10.1.0 | 7.6 | `net10.0` | linux | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Resources/10.1.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Extensions.DependencyInjection.Abstractions` 10.0.0.0 / `Default`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Microsoft.Graph.Authentication | 2.39.0 | 7.6 | `net10.0` | macos | `upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/Core/Azure.Core.dll`
`upstream:Microsoft.Graph.Authentication/2.39.0/Dependencies/System.ClientModel.dll` | `Azure.Core` 1.51.1.0 / `msgraph-load-context`
`System.ClientModel` 1.9.0.0 / `msgraph-load-context` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| ExchangeOnlineManagement | 3.10.1 | 7.6 | `net10.0` | macos | no tracked assembly selected | no tracked assembly observed | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Storage | 9.7.2 | 7.6 | `net10.0` | macos | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll`
`upstream:Az.Storage/9.7.2/Microsoft.OData.Core.dll`
`upstream:Az.Storage/9.7.2/Microsoft.OData.Edm.dll`
`upstream:Az.Storage/9.7.2/Microsoft.Spatial.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/System.ClientModel.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.OData.Core` 7.6.4.0 / `Default`
`Microsoft.OData.Edm` 7.6.4.0 / `Default`
`Microsoft.Spatial` 7.6.4.0 / `Default`
`System.ClientModel` 1.13.0.0 / `AzSharedAssemblyLoadContext` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Accounts | 5.5.2 | 7.6 | `net10.0` | macos | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| MicrosoftTeams | 7.9.0 | 7.6 | `net10.0` | macos | `upstream:MicrosoftTeams/7.9.0/netcoreapp3.1/Microsoft.Identity.Client.dll` | `Microsoft.Identity.Client` 4.82.0.0 / `Default` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | +| Az.Resources | 10.1.0 | 7.6 | `net10.0` | macos | `upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Azure.Core.dll`
`upstream:Az.Resources/10.1.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll`
`upstream:Az.Accounts/5.5.2/lib/netstandard2.0/Microsoft.Identity.Client.Extensions.Msal.dll` | `Azure.Core` 1.57.0.0 / `AzSharedAssemblyLoadContext`
`Microsoft.Extensions.DependencyInjection.Abstractions` 10.0.0.0 / `Default`
`Microsoft.Identity.Client.Extensions.Msal` 4.84.0.0 / `AzSharedAssemblyLoadContext` | **accepted** | 2026-08-10 | [31349870045](https://github.com/SamErde/DLLPickle/actions/runs/31349870045) | + +## Known process-isolation requirement + +Issue #174 remains an expected limitation: ExchangeOnlineManagement and Az.Storage can require incompatible Microsoft.OData major versions. Both import orders are tested, but separate PowerShell processes remain the documented safe boundary until fresh evidence supports a narrower rule. + +## Preserved regression coverage + +- Issue #34: a deterministic negative-control/protected scenario verifies that Microsoft Graph authentication can bind `BaseAbstractApplicationBuilder.WithLogging(IIdentityLogger, Boolean)` after DLLPickle preloading. +- Issue #193: package and runtime checks keep incidental `Microsoft.Extensions.*` assemblies out of the preload bundle. +- PR #215: package, runtime, and ALC checks keep `Azure.Core` and `System.ClientModel` out of DLLPickle so Az.Accounts retains ownership of its private Azure SDK load context. +- Issue #242: a worker-thread assembly-load scenario verifies that DLLPickle does not install PowerShell script-block assembly callbacks that can crash the process. + +## Authenticated read-only release gates + +The deterministic no-auth tier runs in CI. The following credential-dependent commands are not executed without approved credentials and must not be represented as passing: + +- `Microsoft.Graph.Authentication`: `Get-MgContext | Out-Null` +- `ExchangeOnlineManagement`: `Get-EXOMailbox -ResultSize 1 | Out-Null` +- `Az.Storage`: `Get-AzStorageAccount | Select-Object -First 1 | Out-Null` +- `Az.Accounts`: `Get-AzContext | Out-Null` +- `MicrosoftTeams`: `Connect-MicrosoftTeams -AccessTokens @($env:DLLPICKLE_GRAPH_ACCESS_TOKEN, $env:DLLPICKLE_TEAMS_ACCESS_TOKEN) | Out-Null; try { Get-CsTenant | Out-Null } finally { Disconnect-MicrosoftTeams | Out-Null }` +- `Az.Resources`: `Get-AzResource | Select-Object -First 1 | Out-Null` + +These probes permit reads only; writes are not part of the validation tier. PowerShellEditorServices / VS Code coverage for issue #169 also remains an explicit manual gap unless a run artifact records it. + +Before the protected credentialed workflow exists, the release gate may accept one explicitly reviewed manual transition record for version `3.0.0`. That record must match the exact bundle-source fingerprint, cover the three exact Windows profiles and fixed read-only scenarios, contain no credential material or raw service output, record zero writes, and expire exactly 14 days after capture starts. It is transitional compatibility evidence, not least-privilege workload-identity proof. diff --git a/docs/generated/Support-Matrix.md b/docs/generated/Support-Matrix.md new file mode 100644 index 00000000..436a3853 --- /dev/null +++ b/docs/generated/Support-Matrix.md @@ -0,0 +1,17 @@ +# Supported PowerShell runtime matrix + + + +Lifecycle and servicing data last verified: **2026-08-09**. + +| PowerShell line | Exact CI patch | .NET runtime | Bundled TFM | Microsoft lifecycle | Support end | Required OS cells | +|---|---:|---:|---:|---|---:|---| +| 7.4 | 7.4.18 | 8.0.29 | `net8.0` | Supported | 2026-11-10 | Windows, Linux, macOS | +| 7.5 | 7.5.9 | 9.0.18 | `net9.0` | Supported | 2026-11-10 | Windows, Linux, macOS | +| 7.6 | 7.6.4 | 10.0.10 | `net10.0` | Supported | 2028-11-14 | Windows, Linux, macOS | + +This table is the Microsoft-supported runtime contract used by the loader and CI. Upstream module compatibility is a separate evidence question; see [Upstream compatibility evidence](Compatibility-Evidence.md). Exact servicing patches are test pins, not minimum patch claims for end users. + +The module selects a bundle using both the PowerShell minor line and CLR major, then fails closed on a mismatched or unknown pair. Only the rows above are part of the current support contract. + +`multi-pwsh` is optional, checksum-pinned CI provisioning infrastructure. CI invokes the provisioned official `pwsh` executable directly. No multi-pwsh executable, package, manifest dependency, runtime reference, or license payload is published with DLLPickle. diff --git a/docs/plans/2026-08-08-supported-powershell-multitargeting-prompt.md b/docs/plans/2026-08-08-supported-powershell-multitargeting-prompt.md new file mode 100644 index 00000000..6d72711b --- /dev/null +++ b/docs/plans/2026-08-08-supported-powershell-multitargeting-prompt.md @@ -0,0 +1,56 @@ +# Fresh-Conversation Implementation Prompt + +Open a fresh Codex project conversation at the DLLPickle repository root and paste the prompt below. Goal mode is recommended because this is a long-running migration with a concrete validation loop and stopping condition. + +```text +/goal Implement docs/plans/2026-08-08-supported-powershell-multitargeting.md completely and safely. Continue through reviewable checkpoints until the implementation and all locally or CI-runnable validation are complete, or until a genuine authority/credential/external-state blocker requires my input. + +Repository: DLLPickle +Plan: docs/plans/2026-08-08-supported-powershell-multitargeting.md + +Read the complete plan before editing. Then inspect the current repository instructions, worktree status, documentation, workflows, open PRs/issues, and relevant history. Revalidate Microsoft's live PowerShell lifecycle and current servicing patches before relying on the plan's 2026-08-08 snapshot. If the currently supported PowerShell release-line set differs from the plan, stop before changing the settled support set and explain the exact lifecycle delta and implementation impact. + +Implement the plan in small, test-first checkpoints. Keep at most one implementation phase in progress at a time, report concise progress, and validate each phase before advancing. Preserve unrelated user changes. Do not commit, push, rebase, open or modify a PR, merge, publish, release, or change external data unless I explicitly authorize it. + +Non-negotiable requirements: + +1. Within the PowerShell 7 scope, support every and only release lines still supported by Microsoft. The initial expected set is PowerShell 7.4/net8.0, 7.5/net9.0, and 7.6/net10.0. +2. Test the exact current Microsoft-serviced patch for each supported line. Do not infer support for one line from another installed pwsh. +3. Build isolated net8.0, net9.0, and net10.0 dependency bundles while those three PowerShell lines remain supported. Select the bundle using both the running PowerShell minor line and CLR major, and fail closed on mismatches. +4. Keep multi-pwsh strictly optional and CI/test-only. It must not be shipped, added to the module manifest, referenced by production code, added as a runtime/package dependency, or required to build/install/import/use DLLPickle. +5. If multi-pwsh is used to provision tests, pin and checksum it, use a temporary isolated root, and invoke the official installed pwsh/pwsh.exe directly. Do not use multi-pwsh aliases, native host mode, venv startup hooks, or MCP mode as evidence for the stock PowerShell host. Tests must also accept explicit stock PowerShell executable paths so multi-pwsh can be removed or replaced without product changes. +6. Preserve the documented dependency contract: Dependabot patch/minor dependency updates may auto-approve and auto-merge only after every required build, test, compatibility, and dependency-review gate passes. Major dependency updates must become tested draft PRs with per-TFM evidence and must never auto-merge. +7. Make upstream conflict evidence specific to PowerShell line, TFM, OS/platform, module version, selected asset, import order, and AssemblyLoadContext. Cover Microsoft.Graph, Az.Accounts/Az.Resources/Az.Storage, ExchangeOnlineManagement, and MicrosoftTeams. Do not carry net8.0 classifications forward without fresh evidence. +8. Preserve known safety and compatibility lessons from PR #215 and issues #169, #174, #193, #242, and #273. Expected upstream incompatibilities should be documented and tested as limitations, not hidden or falsely reported as fixed. +9. Stabilize Pester/build-tool loading before interpreting dependency failures. Use fresh non-profile child processes and exact tool versions where necessary. +10. Add package inspection proving that multi-pwsh and all CI-only assets are absent from the published artifact. +11. Use natural PowerShell continuation or splatting; do not introduce backtick line continuations. +12. Ordinary unit tests must be deterministic and network-free. Keep live module discovery, external downloads, authentication, and tenant checks in explicit integration/scheduled lanes. +13. Do not use real credentials or perform tenant/external writes without explicit approval. If authenticated read-only validation cannot run, implement and test its harness, then identify the exact missing validation rather than claiming full success. + +Required implementation behavior: + +- Start by making the existing CI/Pester harness deterministic. +- Add shipped runtime-profile policy separately from non-shipped exact test-patch/tooling metadata. +- Multi-target and locked-restore net8.0, net9.0, and net10.0. +- Parameterize all child-process and scenario tooling with an exact PowerShell executable. +- Generate the PowerShell/OS matrix from canonical data and retain stable aggregate required-check names. +- Derive NuGet asset selection from restored project.assets.json rather than a handwritten TFM approximation. +- Update dependency-policy and upstream evidence per profile. +- Deduplicate unchanged drift reports by fingerprint. +- Add generated compatibility documentation and artifact-size reporting. +- Refresh documentation and changelog claims. + +Verification and stopping condition: + +- Analyzer, unit, integration, issue-reproduction, restore, build, pack, loader-selection, policy-schema, workflow-guardrail, documentation-drift, and artifact-inspection tests pass. +- Every available supported PowerShell/OS cell runs the expected stock executable and records PowerShell, CLR, TFM, process path, PSHOME, OS, architecture, selected bundle, and ALC evidence. +- The artifact contains exactly the supported TFM bundles and contains no multi-pwsh files or dependency declarations. +- Dependabot patch/minor and major paths retain their distinct automatic-versus-reviewed behavior. +- All deterministic work is complete, the diff is reviewed with git diff --check, and no unrelated files are changed. +- Credential-dependent or externally blocked validation is either completed with authorization or listed precisely as an outstanding release gate. + +Do not mark the goal complete merely because implementation is extensive or because an external validation lane is unavailable. If genuinely blocked, exhaust safe local work, preserve a clear validation handoff, and ask for the smallest specific input needed. +``` + +If `/goal` is unavailable, enable Goal mode in Codex settings or use the same text as a normal implementation prompt. A normal prompt can still implement the plan; Goal mode mainly supplies persistence and progress controls for the long-running execution. diff --git a/docs/plans/2026-08-08-supported-powershell-multitargeting.md b/docs/plans/2026-08-08-supported-powershell-multitargeting.md new file mode 100644 index 00000000..ecb2a0b6 --- /dev/null +++ b/docs/plans/2026-08-08-supported-powershell-multitargeting.md @@ -0,0 +1,388 @@ +# Microsoft-Supported PowerShell Multi-Targeting Implementation Plan + +**Status:** Ready for implementation + +**Date:** 2026-08-08 + +**Goal:** Replace DLLPickle's single optimistic PowerShell 7.4 / `net8.0` contract with explicitly built, selected, tested, and evidence-backed support for every PowerShell 7 release line that Microsoft still supports. + +**Initial supported set:** PowerShell 7.4, 7.5, and 7.6, targeting `net8.0`, `net9.0`, and `net10.0` respectively. + +**Architecture:** Build one isolated dependency bundle per supported PowerShell/.NET profile. Select the exact bundle from the running PowerShell and CLR versions. Generate CI jobs from a canonical support policy, execute each profile in its own stock `pwsh` process, and maintain conflict evidence independently per profile and operating system. + +**Tech stack:** PowerShell, Pester, Invoke-Build, .NET SDK/MSBuild, NuGet locked restore, GitHub Actions, Dependabot, and optional `multi-pwsh`-assisted CI provisioning. + +--- + +## 1. Settled decisions + +These decisions are inputs to implementation and should not be silently re-adjudicated. + +1. DLLPickle supports only PowerShell 7 release lines that Microsoft currently supports. Windows PowerShell 5.1 remains out of scope. +2. As of 2026-08-08, the supported profiles are: + + | PowerShell | Current documented patch | .NET runtime | TFM | Microsoft lifecycle state | + | --- | --- | --- | --- | --- | + | 7.4 LTS | 7.4.18 | .NET 8 | `net8.0` | Supported; retirement listed in November 2026 | + | 7.5 | 7.5.9 | .NET 9 | `net9.0` | Supported; retirement listed in November 2026 | + | 7.6 LTS | 7.6.4 | .NET 10 | `net10.0` | Supported through November 2028 | + +3. Only the latest Microsoft-serviced patch in each supported release line is an authoritative test target. Exact patch pins are updated automatically after validation. +4. Adding a new PowerShell minor line or removing a retired line changes the distributed support contract and requires maintainer review. It must not auto-merge. +5. `multi-pwsh` is optional CI/test infrastructure only: + + - It is not shipped in the DLLPickle module. + - It is not a module manifest dependency. + - It is not a NuGet runtime or build-output dependency. + - Production code does not invoke or reference it. + - Building, installing, importing, and using DLLPickle do not require it. + - CI may replace it with another official-runtime provisioning mechanism without changing DLLPickle's public or runtime contract. + +6. When `multi-pwsh` provisions a test runtime, tests invoke the installed official `pwsh`/`pwsh.exe` directly. They do not use `pwsh-7.x` aliases, `multi-pwsh host`, native host shims, MCP mode, or virtual-environment startup hooks as evidence for the stock PowerShell host. +7. Routine Dependabot patch/minor dependency PRs may auto-approve and auto-merge only after all required build, test, dependency-review, and compatibility gates pass. +8. Major dependency PRs are tested, converted to draft, documented with per-TFM evidence, and left for maintainer review. They never auto-merge. +9. Conflict classifications are evidence-backed per PowerShell/TFM profile. A `net8.0` result must not be assumed valid for `net9.0` or `net10.0`. +10. Authentication-dependent or tenant-dependent tests do not perform writes and do not use real credentials without explicit approval. Missing credentials must be reported as an outstanding validation gate, not converted into a false success claim. + +## 2. Evidence and references + +Revalidate these sources at implementation start and immediately before release: + +- [Microsoft PowerShell lifecycle](https://learn.microsoft.com/en-us/lifecycle/products/powershell) +- [PowerShell 7.4 release documentation](https://learn.microsoft.com/en-us/powershell/scripting/whats-new/what-s-new-in-powershell-74?view=powershell-7.4) +- [PowerShell 7.5 release documentation](https://learn.microsoft.com/en-us/powershell/scripting/whats-new/what-s-new-in-powershell-75?view=powershell-7.5) +- [PowerShell 7.6 release documentation](https://learn.microsoft.com/en-us/powershell/scripting/whats-new/what-s-new-in-powershell-76?view=powershell-7.6) +- [`awakecoding/multi-pwsh` snapshot](https://github.com/awakecoding/multi-pwsh) +- [Maintained `Devolutions/multi-pwsh` repository](https://github.com/Devolutions/multi-pwsh) +- [`multi-pwsh` host and virtual-environment behavior](https://github.com/Devolutions/multi-pwsh/blob/master/docs/host-and-venv.md) + +The implementation must also preserve lessons established in the repository history: + +- [PR #215](https://github.com/SamErde/DLLPickle/pull/215): identical assembly versions loaded in different ALCs can still break authentication. +- [Issue #193](https://github.com/SamErde/DLLPickle/issues/193): preloading some `Microsoft.Extensions.*` assemblies can break downstream modules. +- [Issue #242](https://github.com/SamErde/DLLPickle/issues/242): PowerShell script-block assembly callbacks can crash the process. +- [Issue #174](https://github.com/SamErde/DLLPickle/issues/174): Az.Storage and ExchangeOnlineManagement can have a genuine OData conflict requiring process isolation. +- [Issue #169](https://github.com/SamErde/DLLPickle/issues/169): the VS Code/PowerShellEditorServices host surface remains unresolved. +- [Issue #273](https://github.com/SamErde/DLLPickle/issues/273): the upstream compatibility baseline is stale and drift reporting is noisy. + +## 3. Current-state gaps + +The current implementation is intentionally single-profile: + +- `src/DLLPickle.Build/DLLPickle.csproj` targets only `net8.0`. +- `src/DLLPickle/Public/Import-DPLibrary.ps1` defaults to `bin/net8.0`. +- `src/DLLPickle/DLLPickle.psd1` and `build/DLLPickle.Settings.ps1` require PowerShell 7.4. +- `global.json` pins a .NET 8 SDK. +- Unit and integration tests contain `net8.0` assumptions. +- `Build Module.yml` tests hosted-runner operating systems but not exact PowerShell release lines. +- `dependency-policy.json` contains one global `net8.0` policy/baseline. +- Upstream inventory scans DLLs recursively without recording the asset TFM actually selected by a given runtime. +- The runtime snapshot tool launches a generic `pwsh` rather than an explicitly supplied executable. +- The TFM-alignment tool uses a partial handwritten compatibility model instead of NuGet's resolved asset graph. +- Current CI can load conflicting Pester versions in one process, obscuring dependency-specific results. + +## 4. Target architecture + +### 4.1 Shipped runtime support map + +Add a small shipped data file such as `src/DLLPickle/SupportedRuntimeProfiles.json` containing only runtime behavior required by the module: + +```json +{ + "schemaVersion": 1, + "profiles": [ + { + "powerShellMajor": 7, + "powerShellMinor": 4, + "dotnetMajor": 8, + "targetFramework": "net8.0" + }, + { + "powerShellMajor": 7, + "powerShellMinor": 5, + "dotnetMajor": 9, + "targetFramework": "net9.0" + }, + { + "powerShellMajor": 7, + "powerShellMinor": 6, + "dotnetMajor": 10, + "targetFramework": "net10.0" + } + ] +} +``` + +Do not place `multi-pwsh` versions, URLs, paths, or test-only patch pins in this shipped file. + +The loader must match both the PowerShell minor line and CLR major. It must fail clearly when: + +- the PowerShell line is unsupported; +- the CLR major does not match the profile; +- the matching TFM directory or required bundle is missing; or +- the policy contains duplicate or malformed profiles. + +### 4.2 CI support and test matrix + +Add a non-shipped file such as `build/powershell-test-matrix.json` containing: + +- exact current patch per supported line; +- Microsoft lifecycle end date and last verification timestamp; +- expected CLR and TFM; +- supported OS/architecture lanes; +- evidence freshness threshold; +- optional pinned `multi-pwsh` version and release checksum metadata. + +Tests must verify that the shipped and CI profile sets agree, while allowing exact patch and CI-tool metadata to remain outside the package. + +### 4.3 Build outputs + +Change the build project to: + +```xml +net8.0;net9.0;net10.0 +``` + +The module artifact must contain isolated output directories for the three profiles. Do not deduplicate files across profiles merely because their hashes currently match; a shared physical load path can change assembly resolution and needs separate evidence. + +## 5. Implementation tasks + +Each task should finish with focused green tests before advancing. Preserve unrelated worktree changes. Do not commit, push, or open a PR unless the maintainer explicitly requests it. + +### Task 0: Refresh live state and establish a clean baseline + +**Primary files:** repository state, live lifecycle sources, current GitHub issues/PRs, current workflows. + +- [ ] Fetch/prune repository metadata without rewriting local work. +- [ ] Confirm the current branch, worktree cleanliness, and divergence from `origin/main`. +- [ ] Revalidate Microsoft's currently supported PowerShell lines and exact current servicing patches. +- [ ] Revalidate the maintained canonical `multi-pwsh` project and current release before pinning it. +- [ ] Inspect open dependency PRs and issue #273 so existing drift is not mistaken for a new multi-TFM regression. +- [ ] Record any change from the initial 7.4/7.5/7.6 assumption and obtain maintainer direction before changing the settled support set. + +### Task 1: Make the existing CI harness deterministic + +**Likely files:** `.github/ci-scripts/Actions_Bootstrap.ps1`, `build/DLLPickle.Build.ps1`, workflow cache definitions, focused workflow tests. + +- [ ] Pin Pester, InvokeBuild, and other build/test tools to explicit versions. +- [ ] Prevent multiple Pester assemblies from being imported into one process. +- [ ] Launch independent build/test stages in fresh `pwsh -NoProfile -NonInteractive` processes where necessary. +- [ ] Include exact tool version, OS, architecture, and PowerShell line in relevant cache keys. +- [ ] Add a regression that detects a mismatched already-loaded Pester assembly. +- [ ] Confirm current Dependabot PR failures report dependency behavior rather than bootstrap nondeterminism. + +**Gate:** Existing single-profile tests are green and reproducible before multi-targeting begins. + +### Task 2: Add support-policy data and validation + +**Create:** + +- `src/DLLPickle/SupportedRuntimeProfiles.json` +- `build/powershell-test-matrix.json` +- focused policy/schema tests under `tests/Unit/` + +**Modify:** manifest/build settings and any package-copy allowlists needed for the small runtime profile file. + +- [ ] Write failing schema, uniqueness, and cross-file-alignment tests. +- [ ] Add the shipped runtime profile and non-shipped CI matrix. +- [ ] Validate PowerShell minor, CLR major, TFM, supported OS, exact patch, lifecycle date, and evidence timestamp fields. +- [ ] Add a release-time lifecycle check that fails if the package still claims an expired Microsoft support line. +- [ ] Add a scheduled warning before an impending retirement. +- [ ] Ensure an exact patch-only CI matrix update does not alter the shipped runtime contract. + +### Task 3: Multi-target the dependency bundle and loader + +**Likely files:** + +- `src/DLLPickle.Build/DLLPickle.csproj` +- `packages.lock.json` +- `global.json` +- `build/DLLPickle.Build.ps1` +- `build/DLLPickle.Settings.ps1` +- `src/DLLPickle/DLLPickle.psd1` +- `src/DLLPickle/Public/Import-DPLibrary.ps1` +- TFM and import unit/integration tests + +- [ ] Write failing tests for all three profile mappings and mismatch cases. +- [ ] Target `net8.0`, `net9.0`, and `net10.0` with locked restore. +- [ ] Pin a suitable .NET 10 SDK capable of building all three TFMs. +- [ ] Make build/copy/pack tasks handle all declared TFMs without hardcoded `net8.0` paths. +- [ ] Make the loader select the exact profile from PowerShell and CLR versions. +- [ ] Set the manifest minimum to the oldest currently supported PowerShell line while enforcing the exact supported set in the loader. +- [ ] Keep common dependency versions initially; introduce conditional per-TFM versions only when evidence requires them. +- [ ] Verify every packaged TFM has the expected dependency set and no undeclared spillover. + +### Task 4: Add optional exact-runtime provisioning + +**Create or modify:** a CI helper such as `tools/Install-DLLPickleTestPowerShell.ps1` and focused tests. + +The helper should accept an explicit provider or executable path so tests are not coupled to `multi-pwsh`: + +```text +-PowerShellExecutable Use an already-provisioned stock executable +-Provider MultiPwsh Optionally provision through multi-pwsh +-Provider DirectArchive Provision from official PowerShell release archives +``` + +- [ ] Pin and checksum-verify any `multi-pwsh` release used in CI. +- [ ] Treat pre-1.0 `multi-pwsh` minor updates as reviewed toolchain changes. +- [ ] Install under a runner-temporary explicit root with no persistent PATH mutation. +- [ ] Derive the official executable path from the installation root/version, not from alias output. +- [ ] Invoke the real installed `pwsh`/`pwsh.exe` directly. +- [ ] Verify exact PowerShell version, CLR description/version, `$PSHOME`, process path, OS, architecture, and expected TFM before running product tests. +- [ ] Reject a `multi-pwsh` host shim or any executable path outside the expected official payload root. +- [ ] Cache official archives by provider version, OS, architecture, and exact PowerShell patch. +- [ ] Prove that the build and tests can run with an explicit executable path when `multi-pwsh` is absent. +- [ ] Add an artifact/package inspection asserting no `multi-pwsh` file, package, manifest dependency, or runtime reference is shipped. + +### Task 5: Generate the exact PowerShell/OS CI matrix + +**Likely files:** `.github/workflows/Build Module.yml`, reusable workflow/scripts, workflow guardrail tests. + +Generate nine authoritative cells from `build/powershell-test-matrix.json`: + +```text +PowerShell 7.4 × Windows, Linux, macOS +PowerShell 7.5 × Windows, Linux, macOS +PowerShell 7.6 × Windows, Linux, macOS +``` + +Each cell must: + +- [ ] provision or receive one exact stock PowerShell executable; +- [ ] use an isolated `PSModulePath` and fresh process; +- [ ] verify runtime identity before testing; +- [ ] run unit, integration, packaging, loader-selection, and known-regression tests; +- [ ] capture assembly/ALC snapshots and the selected bundle path; and +- [ ] upload a structured result artifact. + +Preserve stable required-check names: + +- `Build gate` aggregates build/runtime matrix results. +- `Validate upstream compatibility tooling` aggregates policy, evidence, drift, and freshness results. +- `dependency-review` remains required. + +The matrix must cover regressions represented by issues/PRs #34, #193, #215, and #242. Issue #174 should be represented as an expected conflict/limitation until fresh evidence proves otherwise. PowerShellEditorServices/VS Code coverage should address issue #169 or retain it as an explicit gap. + +### Task 6: Make dependency and conflict evidence profile-aware + +**Likely files:** + +- `build/dependency-policy.json` +- `tools/Get-DLLPickleUpstreamInventory.ps1` +- `tools/Get-DLLPickleRuntimeAssemblySnapshot.ps1` +- `tools/Test-DLLPickleTfmAlignment.ps1` +- `tools/Update-DLLPickleDependencyPins.ps1` +- `tests/Integration/Invoke-DLLPickleScenario.ps1` +- upstream compatibility workflow and tests + +- [ ] Key preload, block, and known-conflict decisions by PowerShell line, TFM, OS/platform, module set, and import order. +- [ ] Record umbrella module version and the constituent module that actually ships each assembly. +- [ ] Record module manifest PowerShell compatibility and the newest release compatible with each profile. +- [ ] Record the asset path/TFM actually selected by the tested runtime rather than recursively mixing every DLL in the module directory. +- [ ] Record assembly name, version, hash, path, ALC, OS, architecture, and probe command. +- [ ] Parameterize child-process tooling with an exact `-PowerShellExecutable`. +- [ ] Derive NuGet compatibility from restored `project.assets.json` instead of extending the handwritten regex model. +- [ ] Complete lazy-load probe commands for ExchangeOnlineManagement and MicrosoftTeams. +- [ ] Test both relevant import orders, with and without DLLPickle. +- [ ] Separate deterministic import/no-auth evidence from credential-dependent authenticated smoke evidence. +- [ ] Re-adjudicate current issue #273 drift once per profile. +- [ ] Deduplicate drift reporting by fingerprint so an unchanged finding does not generate repeated comments. + +Initial monitored module families include: + +- Microsoft.Graph.Authentication / Microsoft.Graph +- Az.Accounts, Az.Resources, Az.Storage / Az +- ExchangeOnlineManagement +- MicrosoftTeams + +If the newest upstream release does not support a still-supported PowerShell line, test the newest compatible release and document the upstream limitation explicitly. + +### Task 7: Preserve and extend dependency automation + +**Likely files:** `.github/dependabot.yml`, `.github/workflows/Dependabot-Auto-Approve.yml`, workflow guardrail tests, dependency documentation. + +- [ ] Preserve daily NuGet patch/minor grouping and the exact Dependabot actor/author checks. +- [ ] Expand the allowed dependency-file set only as needed for multi-target project/lock files. +- [ ] Require the complete TFM/runtime matrix, upstream-policy gate, build gate, and dependency review before auto-merge completes. +- [ ] Keep patch/minor auto-approval and auto-merge registration after all checks pass. +- [ ] Keep major updates draft-only and never auto-merged. +- [ ] Attach a per-TFM major-update report containing resolved graph, selected assets, added/removed assemblies, conflict-surface delta, and scenario outcomes. +- [ ] Require review for a preload/block classification change, a new conditional TFM pin, or a material size-budget breach even if the package version is nominally minor. +- [ ] Preserve `deps:` to minor module-release behavior and `breaking:` for maintainer-approved major dependency changes. + +### Task 8: Generate documentation and enforce artifact size + +**Likely files:** `README.md`, `docs/Architecture.md`, `docs/Deep-Dive.md`, `docs/DEPENDENCIES.md`, `docs/Troubleshooting.md`, `CHANGELOG.md`, generated compatibility artifacts. + +- [ ] Replace single-`net8.0` claims with the current generated support matrix. +- [ ] Document Microsoft-supported versus upstream-module-supported combinations separately. +- [ ] Generate compatibility rows containing module/version, PowerShell, TFM, OS, selected asset, assembly/ALC result, verdict, evidence date, and run identifier. +- [ ] Document known process-isolation requirements rather than implying every module combination can coexist. +- [ ] Add documentation drift tests against the support/profile data. +- [ ] Generate unpacked and compressed size reports per TFM and for the full release artifact. +- [ ] Commit an approved size baseline and show deltas in dependency PRs. +- [ ] Route unexpected size growth to review instead of unattended merge. +- [ ] State explicitly that `multi-pwsh` is optional CI tooling and is absent from the published module. + +### Task 9: Full validation and release-readiness review + +- [ ] Run analyzer, unit tests, integration tests, issue reproductions, locked restore, complete build, and packaging checks. +- [ ] Run all available exact-runtime cells locally or through GitHub Actions. +- [ ] Confirm the package contains only `net8.0`, `net9.0`, and `net10.0` while those lines remain Microsoft-supported. +- [ ] Confirm no `multi-pwsh` executable, NuGet package, module dependency, code reference, or license payload is present in the artifact. +- [ ] Confirm every claimed runtime reports the expected PowerShell, CLR, TFM, `$PSHOME`, and process executable. +- [ ] Refresh Microsoft lifecycle state immediately before release. +- [ ] If credentials and approval are available, run the authenticated read-only validation tier; otherwise document the exact unexecuted scenarios as a release gate. +- [ ] Run `git diff --check`, review the complete diff, and verify no unrelated or generated temporary files remain. + +## 6. Automation policy + +### 6.1 PowerShell runtime patch updates + +A scheduled job discovers the newest GA patch within each declared supported line and opens a PR updating only test-matrix/evidence pins. That PR may merge automatically after all supported profiles pass. The workflow must pin the discovered exact version in the PR; required CI must not use a floating `7.4`, `7.5`, or `7.6` selector whose result can change between reruns. + +### 6.2 New or retired PowerShell lines + +Detection is automatic; support-contract changes are reviewed. + +- A new supported minor line opens a proposal PR or issue containing the PowerShell/.NET/TFM mapping, package-size estimate, build results, and initial upstream conflict evidence. +- An approaching retirement opens a warning before the lifecycle deadline. +- Removal of a retired line changes the loader, package contents, documentation, and support floor and therefore requires a reviewed release decision. +- The release workflow fails closed if the package still claims a line past the verified Microsoft support end. + +### 6.3 Dependency updates + +- Patch/minor: automatic PR, full multi-profile tests, auto-approval, auto-merge after all gates. +- Major: automatic draft PR, full multi-profile tests and structured evidence, maintainer review, no auto-merge. +- Policy/TFM/size change: manual review regardless of nominal dependency update type. + +## 7. Definition of done + +- [ ] PowerShell 7.3 and `net7.0` are absent from code, tests, artifacts, and support claims. +- [ ] The published module contains one isolated payload for every and only currently Microsoft-supported PowerShell 7 line in scope. +- [ ] The loader selects the exact TFM using both PowerShell and CLR versions and fails closed on mismatches. +- [ ] Every claimed PowerShell/OS cell executes the stock official `pwsh` binary for its exact pinned servicing patch. +- [ ] Test logs and artifacts record PowerShell, CLR, TFM, executable path, `$PSHOME`, OS, architecture, and selected bundle. +- [ ] Conflict evidence is profile-aware and does not use a TFM-blind global baseline. +- [ ] Graph, Az, ExchangeOnlineManagement, and MicrosoftTeams have current per-profile compatibility evidence or an explicit, evidenced limitation. +- [ ] Routine Dependabot patch/minor PRs merge unattended only after all required gates pass. +- [ ] Major updates remain draft until maintainer review. +- [ ] Runtime patch updates are proposed and validated automatically; new/retired support lines are reviewed. +- [ ] Documentation and artifact-size reports are generated and checked for drift. +- [ ] `multi-pwsh` is absent from the published artifact and every runtime/build dependency declaration. +- [ ] The project can build and test from explicit stock PowerShell executable paths with `multi-pwsh` unavailable. +- [ ] Any credential-dependent validation not executed is identified precisely and is not represented as passing. + +## 8. Recommended PR sequence + +1. CI tool determinism and Pester isolation. +2. Support-policy schemas and profile-alignment tests without runtime behavior changes. +3. Multi-TFM build outputs and exact loader selection. +4. Optional runtime provisioner and exact stock-executable validation. +5. Nine-cell PowerShell/OS matrix and stable aggregate gates. +6. Profile-aware upstream inventory, policy, evidence, and issue #273 re-adjudication. +7. Dependabot report/gate updates, documentation generation, and artifact-size policy. +8. Final lifecycle refresh, authenticated validation where authorized, and release preparation. + +Keep these changes reviewable and independently green. Do not combine a support-contract change with unrelated feature work. diff --git a/docs/plans/2026-08-09-credentialed-authentication-test-environment.md b/docs/plans/2026-08-09-credentialed-authentication-test-environment.md new file mode 100644 index 00000000..6496bc62 --- /dev/null +++ b/docs/plans/2026-08-09-credentialed-authentication-test-environment.md @@ -0,0 +1,347 @@ +# Draft plan: credentialed read-only authentication test environment + +**Status:** Draft for security, tenant, and repository-owner review + +**Date:** 2026-08-09 + +**Scope:** Microsoft Graph, Exchange Online, Azure PowerShell, and Microsoft Teams authentication gates for DLLPickle + +**External changes performed by this document:** None + +## 1. Objective + +Create an approval-gated environment that can run DLLPickle's authenticated compatibility tier without interactive sign-in, tenant writes, long-lived client secrets, or misleading pass results when credentials are absent. + +The environment must prove that the supported DLLPickle runtime profiles can authenticate and execute the exact read-only probes declared in `build/dependency-policy.json` after the relevant modules and DLLPickle are loaded in both monitored import orders. + +This is separate from the deterministic, zero-credential import tier. A missing credential, missing role, conditional-access block, unsupported authentication mode, or unexecuted probe remains a failed or outstanding release gate. + +## 2. Current authenticated gates + +| Module family | Current authenticated read-only probe | What it proves | +| --- | --- | --- | +| Microsoft.Graph.Authentication | `Get-MgContext \| Out-Null` | A Graph authentication context was established in the current process. | +| ExchangeOnlineManagement | `Get-EXOMailbox -ResultSize 1 \| Out-Null` | EXO app-only authentication and a limited mailbox read succeed. | +| Az.Storage | `Get-AzStorageAccount \| Select-Object -First 1 \| Out-Null` | Azure Resource Manager authentication can enumerate storage-account control-plane metadata. | +| Az.Accounts | `Get-AzContext \| Out-Null` | An Azure PowerShell process-scoped context was established. | +| MicrosoftTeams | `Get-CsTenant \| Out-Null` | Teams application authentication and a tenant read succeed. | +| Az.Resources | `Get-AzResource \| Select-Object -First 1 \| Out-Null` | Azure Resource Manager authentication can enumerate resource metadata. | + +The Graph probe currently proves context establishment, not an API read. Before calling the Graph gate complete, decide whether to retain that narrow contract or add a separately reviewed read probe such as an organization read. Do not silently broaden it in the workflow. + +## 3. Non-goals and safety invariants + +- Do not create, update, delete, assign, invite, send, publish, or consent to tenant data during a test run. +- Do not run the credentialed job for pull requests from forks or for unreviewed code. +- Do not expose access tokens, certificates, assertion tokens, tenant identifiers that are classified as secrets, or command output containing customer data. +- Do not store a client secret in the repository. Prefer short-lived OpenID Connect tokens. A certificate fallback requires explicit approval and a rotation plan. +- Do not use Global Administrator, Owner, Contributor, Exchange Administrator, or another broad role merely to make the probes pass. +- Do not enable a schedule until the manual workflow has been reviewed, exercised, and accepted. +- Do not represent skipped probes as passing compatibility evidence. + +## 4. Recommended architecture + +### 4.1 GitHub control plane + +Create a GitHub environment named `authenticated-readonly` with: + +- required reviewer approval; +- self-review prevention where the repository plan supports it; +- deployment branches restricted to `main` and explicitly approved test branches; +- environment-scoped variables and secrets only; +- no automatic execution for forked pull requests; +- a workflow with `contents: read` and `id-token: write`, and no repository-writing permissions. + +GitHub environments can withhold environment secrets until required reviewers approve a job. See [Deployments and environments](https://docs.github.com/en/actions/reference/workflows-and-actions/deployments-and-environments). + +Start with `workflow_dispatch` only. A release workflow may call the credentialed workflow later, but only after the environment approval and evidence-redaction controls are accepted. + +### 4.2 Identity separation + +Prefer two dedicated workload identities to keep Azure and Microsoft 365 authorization independently revocable: + +1. `dllpickle-azure-readonly` + - Federated to this repository and the `authenticated-readonly` GitHub environment. + - Azure `Reader` role at a dedicated test resource group, not the subscription root. + - The resource group contains at least one storage account and one harmless ARM resource so enumeration probes exercise real code paths. + +2. `dllpickle-m365-auth-probe` + - Federated to the same protected GitHub environment if Graph, EXO, and Teams token exchange is proven to work for the pinned module versions. + - Assigned only the application permissions and service-specific RBAC needed for the approved probes. + - No delegated user credentials. + +Microsoft documents GitHub-to-Azure workload identity federation through OIDC in [Authenticate to Azure from GitHub Actions by OpenID Connect](https://learn.microsoft.com/en-us/azure/developer/github/connect-from-azure-openid-connect). Azure's built-in `Reader` role permits control-plane reads without changes; scope it to the dedicated resource group. See [Azure built-in roles for General](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/general). + +### 4.3 Authentication mechanism decision + +Preferred path: + +- Use GitHub OIDC to obtain short-lived workload tokens. +- Use `azure/login` with Azure PowerShell session support for Az.Accounts/Az.Resources/Az.Storage. +- Exchange the workload identity for service-specific access tokens for Graph, Exchange Online, and Teams, then pass tokens directly to the modules in process scope. + +The feasibility spike must prove all of the following before this becomes the accepted design: + +- `Connect-MgGraph -AccessToken ... -ContextScope Process` accepts the acquired Graph token under every pinned PowerShell profile. +- `Connect-ExchangeOnline -AccessToken ... -Organization ` accepts the app-only Exchange token. +- `Connect-MicrosoftTeams -AccessTokens @(, )` accepts the required token pair. +- The token audience, cloud endpoints, and module versions are recorded without logging token values. + +First-party references: + +- [Microsoft Graph PowerShell authentication commands](https://learn.microsoft.com/en-us/powershell/microsoftgraph/authentication-commands?view=graph-powershell-1.0) +- [Exchange Online app-only authentication](https://learn.microsoft.com/en-us/powershell/exchange/app-only-auth-powershell-v2?view=exchange-ps) +- [Teams PowerShell application-based authentication](https://learn.microsoft.com/en-us/microsoftteams/teams-powershell-application-authentication) + +Fallback path, requiring a separate approval: + +- Use one short-lived X.509 certificate per Microsoft 365 test identity. +- Store the encrypted PFX and its password only as protected environment secrets, import it into an ephemeral certificate store, and remove it in an `always()` cleanup step. +- Record owner, expiry, rotation date, and emergency revocation instructions before first use. + +Graph, Exchange Online, and Teams all document certificate-based application authentication. The fallback is operationally simpler but introduces a long-lived credential and therefore is not the default. + +## 5. Least-privilege authorization work + +The tenant administrator must review and execute all consent and role assignments. Repository automation must not grant its own permissions. + +### 5.1 Microsoft Graph + +- Start with only the application permission needed by the approved Graph read probe. +- If the probe remains `Get-MgContext`, document that it validates authentication context only. +- If an organization read is approved, evaluate `Organization.Read.All` application permission and grant admin consent only after review. +- Use `-ContextScope Process` and call `Disconnect-MgGraph` during cleanup. + +### 5.2 Exchange Online + +- Configure app-only Exchange authentication using the tenant's primary `.onmicrosoft.com` organization name. +- Review the `Exchange.ManageAsApp` application permission requirement from Microsoft's app-only guidance. +- Use Exchange Online application RBAC or a custom least-privilege management role that permits the specific `Get-EXOMailbox -ResultSize 1` read. Do not assign Exchange Administrator solely for this test. +- Prove that the service principal can read one mailbox object and cannot execute a selected write negative-control command. +- Call `Disconnect-ExchangeOnline -Confirm:$false` during cleanup. + +### 5.3 Azure PowerShell + +- Assign Azure `Reader` at the dedicated test resource-group scope. +- Confirm the role exposes storage-account and resource control-plane metadata but no data-plane content. +- Include at least one storage account so `Get-AzStorageAccount` cannot pass vacuously because the environment is empty. +- Use a process-scoped context and clear it during cleanup. + +### 5.4 Microsoft Teams + +- Review the Teams application-authentication permission table for the pinned MicrosoftTeams module. +- `Get-CsTenant` requires a tenant read. Evaluate `Organization.Read.All` and the narrowest supported Microsoft Entra/Teams RBAC role for this cmdlet. +- Do not copy the broad permission set documented for all non-`*-Cs` cmdlets when only `Get-CsTenant` is in scope. +- Prove both the positive read and a denied write negative control. +- Call `Disconnect-MicrosoftTeams` during cleanup. + +## 6. Environment configuration contract + +Use environment variables for non-secret identifiers and environment secrets only where a provider cannot use OIDC directly. + +Candidate environment variables: + +- `DLLPICKLE_AUTH_TENANT_ID` +- `DLLPICKLE_AUTH_AZURE_CLIENT_ID` +- `DLLPICKLE_AUTH_M365_CLIENT_ID` +- `DLLPICKLE_AUTH_SUBSCRIPTION_ID` +- `DLLPICKLE_AUTH_RESOURCE_GROUP` +- `DLLPICKLE_AUTH_EXO_ORGANIZATION` +- `DLLPICKLE_AUTH_CLOUD` with an allow-listed default such as `AzureCloud` + +Certificate fallback secrets, if explicitly approved: + +- `DLLPICKLE_AUTH_M365_PFX_BASE64` +- `DLLPICKLE_AUTH_M365_PFX_PASSWORD` + +Never accept arbitrary connection commands, scopes, resource URLs, or probe script text from workflow inputs. Workflow inputs may select a reviewed profile or scenario identifier only. + +## 7. Workflow and test-harness implementation + +### Phase A: offline contract tests + +1. Add a schema for the credentialed environment variables and approved command allow-list. +2. Add zero-network unit tests for missing values, malformed GUIDs, unsupported clouds, and forbidden commands. +3. Add a dry-run mode that prints probe identifiers and expected permissions but never token values or connection arguments containing credentials. +4. Verify the ordinary unit and integration suites remain zero-credential and zero-network. + +### Phase B: manual identity feasibility spike + +1. Configure the protected GitHub environment and federated identity manually. +2. Run one exact Windows profile with no DLLPickle import to prove each provider's authentication mode independently. +3. Run the approved read probes and negative write controls. +4. Capture only sanitized identity metadata: tenant hash or approved tenant label, client ID if non-secret, token audience, expiry time, authentication mode, module version, command name, success/error type, and workflow run ID. +5. Revoke the federated credential or certificate after the spike if the design is rejected. + +### Phase C: DLLPickle compatibility matrix + +For each supported PowerShell release line, use the exact patch from `build/powershell-test-matrix.json` and a fresh process for every scenario: + +1. Module authentication without DLLPickle. +2. DLLPickle first, then module connection and read probe. +3. Module connection first, then DLLPickle and read probe where the module supports that order. +4. Both monitored cross-module import orders from `build/dependency-policy.json`. +5. Assembly/ALC snapshot before authentication, after connection, and after the read probe. + +Begin with the three Windows profiles. Expanding credentialed execution to Linux and macOS requires a separate support and risk review because it increases the number of environments receiving tenant tokens. + +### Phase D: release integration + +Only after the manual matrix is accepted: + +- add the credentialed tier as the reusable, environment-gated `.github/workflows/Authenticated-Compatibility.yml` workflow; +- upload exactly one sanitized `authenticated-compatibility-evidence` artifact after all required probes and redaction checks pass; +- keep `workflow_dispatch` available for focused reruns; +- preserve the existing release gate that requires the successful workflow and unexpired evidence artifact for the exact reviewed candidate commit; +- do not allow the job to auto-approve, merge, publish, or mutate issues; +- set a documented evidence freshness window. + +### Temporary 3.0.0 transition bridge + +Before the protected environment is available, the initial multi-target major +release may use sanitized evidence collected by the interactive harness. This is +not an authenticated-gate waiver. The release validator requires: + +- the exact published-bundle source fingerprint, including packaging logic and + pinned build tooling, recorded at capture time; +- one byte-level prepared module-inventory fingerprint per exact runtime profile; +- release version `3.0.0` and no other version; +- all fixed module-only, DLLPickle-first, module-first, and cross-import-order + read scenarios under PowerShell 7.4, 7.5, and 7.6 on Windows x64; +- a real delegated Graph `/me` read in addition to the policy's context probe; +- passing EXO mailbox, Azure context/resource/storage-account, and Teams tenant + reads; +- normalized before-authentication, after-connection, and after-probe ALC + snapshots; +- zero writes and no credential material or raw provider output; +- explicit maintainer acceptance with a confidence level; and +- expiry exactly 14 days after the capture session starts, without renewal when + a partial session resumes. + +The record explicitly states that delegated interactive authentication is not +least-privilege workload-identity proof. Any bundle change, skipped or failed +probe, missing profile, different release version, expiry, or missing acceptance +closes the bridge. The future protected workflow remains the preferred route and +supersedes this transition mechanism. + +### Interactive transition runbook + +Run these commands in a normal interactive PowerShell terminal from a clean, +synced checkout of the reviewed PR branch. Do not paste tokens or passwords into +the command line. + +```powershell +pwsh -NoLogo -NoProfile -File .\tools\Initialize-DLLPickleManualAuthenticatedCompatibility.ps1 +``` + +The initialization step performs no provider authentication. It builds +DLLPickle, installs the checksum-pinned Windows x64 PowerShell executables, and +downloads the latest compatible monitored modules into the gitignored +`artifacts/manual-authenticated` directory. + +If the interactive Azure account exposes more than one subscription, set the +intended test subscription for the current terminal without committing it: + +```powershell +$env:DLLPICKLE_MANUAL_AZURE_SUBSCRIPTION_ID = '' +``` + +Then start or resume the authenticated capture: + +```powershell +pwsh -NoLogo -NoProfile -File .\tools\Invoke-DLLPickleManualAuthenticatedCompatibility.ps1 +``` + +The harness opens only the provider sign-in experiences. Each of the 14 fixed +scenarios runs in a fresh process for each of the three exact profiles. Passing +scenario checkpoints are reused on rerun only while their complete prepared +module-inventory fingerprint still matches, so an authorization failure or an +interrupted session does not require repeating unchanged completed scenarios. +The fixed Az scenarios use delegated device-code authentication so the capture +does not depend on the terminal host's WAM integration or change persisted Az +configuration. Follow the displayed device-login instructions in a browser. +To diagnose one cell first, use the optional `-ProfileKey` and `-ScenarioId` +filters; the candidate remains incomplete until all 42 checkpoints exist. + +After the candidate is complete, review +`artifacts/manual-authenticated/manual-authenticated-evidence.candidate.json`. +It must contain no account, tenant, subscription, mailbox, resource, token, or +raw error/service output. Acceptance is a separate, confirmation-gated action: + +```powershell +$AcceptanceParameters = @{ + CandidateEvidencePath = '.\artifacts\manual-authenticated\manual-authenticated-evidence.candidate.json' + AcceptedBy = 'SamErde' + Confidence = 'high' +} +& .\tools\Set-DLLPickleManualAuthenticatedEvidenceAcceptance.ps1 @AcceptanceParameters +``` + +This writes `build/authenticated-evidence/initial-multitarget-major.json` only +after the pending candidate validates. Commit that one sanitized file only after +review. Never commit the gitignored work directory or provider caches. + +## 8. Evidence schema and redaction + +Each probe record should include: + +- PowerShell exact version, CLR version, TFM, OS, architecture, `$PSHOME`, and executable path; +- DLLPickle version/commit and selected bundle; +- module name/version and import order; +- connection method identifier, token audience identifier, and token expiry timestamp; +- probe command identifier, result, duration, and normalized error type; +- tracked assembly name/version/hash/path/ALC before and after authentication; +- `WritesPerformed: false`; +- workflow run URL and environment name; +- explicit `Executed`, `Skipped`, or `Blocked` status. + +Redact access tokens, authorization headers, certificate bytes/passwords, mailbox identities, tenant domains where required, subscription/resource names where required, and raw command output. Upload JSON evidence only after a redaction test passes. + +## 9. Negative controls + +The feasibility run is not accepted without evidence that the identity is constrained: + +- Azure: inspect effective roles and actions at the assigned scope and use any available non-mutating authorization query. `-WhatIf` may remain a local no-mutation safeguard, but it is not authorization evidence and does not prove that the identity lacks write permission. Do not perform a real write merely to prove denial. +- Graph: inspect granted application permissions and verify no write permission is consented. +- Exchange: inspect the assigned application RBAC role and confirm it contains only required read cmdlets/parameters. +- Teams: inspect API permissions and assigned role; do not execute a state-changing Teams cmdlet. +- Workflow: confirm forked PRs cannot obtain the environment or OIDC subject, and confirm environment approval is required before token issuance. + +## 10. Cleanup, rotation, and incident response + +Every job uses an `always()` cleanup step to disconnect providers, clear process-scoped contexts, remove temporary certificate material, and delete temporary module/token caches. Evidence upload occurs after redaction and before runner teardown. + +Before enabling the environment, document: + +- identity owners and backup owners; +- federated credential subject and audiences; +- role/permission inventory; +- certificate expiry and rotation if the fallback is used; +- normal revocation procedure; +- emergency disable procedure for the GitHub environment and Entra service principals; +- audit-log locations and review cadence. + +## 11. Decisions requiring explicit approval + +1. Which tenant and dedicated Azure resource group may be used? +2. Is GitHub OIDC the required design, or may the certificate fallback be prototyped if a module cannot consume federated access tokens? +3. Should Graph remain a context-only gate or add a real organization read? +4. What exact Exchange application RBAC role is acceptable for `Get-EXOMailbox -ResultSize 1`? +5. What exact Teams role is the least privilege supported for `Get-CsTenant`? +6. Who reviews the `authenticated-readonly` GitHub environment? +7. What evidence fields require hashing or redaction for the selected tenant? +8. What freshness window is required before release? +9. Is credentialed coverage initially Windows-only across three PowerShell profiles, or is cross-platform token exposure approved? + +## 12. Definition of done + +- The GitHub environment is protected and cannot be used by forked or unreviewed code. +- No long-lived client secret is used. +- Every permission and role is documented with scope and owner. +- All six configured authenticated read probes execute under each approved profile, or the exact blocked probe is recorded as a release gate. +- Both monitored import orders and with/without-DLLPickle scenarios produce sanitized ALC evidence. +- No test performs a tenant or Azure resource write. +- Missing credentials and denied permissions fail closed. +- Tokens and tenant data are absent from logs and artifacts. +- Revocation and cleanup are demonstrated. +- Maintainers explicitly accept the evidence before changing `not-run-no-approved-credentials` status in `build/dependency-policy.json`. diff --git a/global.json b/global.json index 391ba3c2..c66295ec 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,7 @@ { "sdk": { - "version": "8.0.100", - "rollForward": "latestFeature" + "version": "10.0.302", + "rollForward": "disable", + "allowPrerelease": false } } diff --git a/src/DLLPickle.Build/DLLPickle.csproj b/src/DLLPickle.Build/DLLPickle.csproj index 6e695be5..a8e04a34 100644 --- a/src/DLLPickle.Build/DLLPickle.csproj +++ b/src/DLLPickle.Build/DLLPickle.csproj @@ -1,7 +1,7 @@ - net8.0 + net8.0;net9.0;net10.0 false false 2008 @@ -10,14 +10,14 @@ - - - + + " + } + + It 'finalizes a patch-only matrix proposal only after every lane reports one consistent runtime' { + $Fixture = Get-SupportUpdateFixture -Root $TestDrive -ReleaseVersions @('7.4.18', '7.5.10', '7.6.4') + $ReportPath = Join-Path $TestDrive 'patch-report.json' + $null = & $script:DiscoveryPath -TestMatrixPath $Fixture.MatrixPath -ReleaseDataPath $Fixture.ReleasePath -LifecycleDataPath $Fixture.LifecyclePath -OutputPath $ReportPath -AsOfUtc '2026-08-08T00:00:00Z' + $CandidatePath = Join-Path $TestDrive 'candidate-matrix.json' + $Preparation = & $script:MatrixUpdatePath -TestMatrixPath $Fixture.MatrixPath -UpdateReportPath $ReportPath -OutputPath $CandidatePath -PrepareCandidate + $Preparation.Mode | Should -Be 'CandidatePreparation' + $PreparedMatrix = Get-Content -LiteralPath $CandidatePath -Raw | ConvertFrom-Json + $PreparedMatrix.candidateValidationPending | Should -BeTrue + @($PreparedMatrix.profiles | Where-Object powerShellVersion -EQ '7.5.10')[0].dotnetRuntimeVersion | Should -BeNullOrEmpty + $Preparation.UpdatedLines[0].DotNetRuntimeVersion | Should -Be 'pending-runtime-identity' + + $IdentityDirectory = Join-Path $TestDrive 'identities' + $null = New-Item -Path $IdentityDirectory -ItemType Directory + foreach ($Platform in @('windows', 'linux', 'macos')) { + @{ + PowerShellVersion = '7.5.10' + DotNetVersion = '9.0.19' + Platform = $Platform + Architecture = 'x64' + TargetFramework = 'net9.0' + } | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $IdentityDirectory "$Platform.json") -Encoding UTF8 + } + @{ PowerShellVersion = '7.5.10'; Platform = 'windows' } | + ConvertTo-Json | + Set-Content -LiteralPath (Join-Path $IdentityDirectory 'malformed.json') -Encoding UTF8 + $FinalPath = Join-Path $TestDrive 'final-matrix.json' + $IdentityWarnings = @() + $Final = & $script:MatrixUpdatePath -TestMatrixPath $Fixture.MatrixPath -UpdateReportPath $ReportPath -OutputPath $FinalPath -RuntimeIdentityPath $IdentityDirectory -VerifiedAtUtc '2026-08-08T12:34:56Z' -WarningVariable IdentityWarnings + $Final.Mode | Should -Be 'VerifiedProposal' + ($IdentityWarnings -join [Environment]::NewLine) | Should -Match 'malformed\.json.*DotNetVersion.*Architecture' + $Updated = Get-Content -LiteralPath $FinalPath -Raw | ConvertFrom-Json + @($Updated.profiles | Where-Object powerShellVersion -EQ '7.5.10') | Should -HaveCount 1 + @($Updated.profiles | Where-Object powerShellVersion -EQ '7.5.10')[0].dotnetRuntimeVersion | Should -Be '9.0.19' + @($Updated.archiveAssets | Where-Object powerShellVersion -EQ '7.5.10') | Should -HaveCount 3 + ([datetime]$Updated.lastVerifiedUtc).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') | Should -Be '2026-08-08T12:34:56Z' + $Updated.PSObject.Properties.Name | Should -Not -Contain 'candidateValidationPending' + } + + It 'routes a new GA minor line to support-contract review and fails release-current validation' { + $Fixture = Get-SupportUpdateFixture -Root $TestDrive -ReleaseVersions @('7.4.18', '7.5.9', '7.6.4', '7.7.0') -NewLineStartDate '7/20/2026 8:00:00 AM' + $Parameters = @{ + TestMatrixPath = $Fixture.MatrixPath + ReleaseDataPath = $Fixture.ReleasePath + LifecycleDataPath = $Fixture.LifecyclePath + OutputPath = Join-Path $TestDrive 'new-line.json' + AsOfUtc = '2026-08-08T00:00:00Z' + RequireCurrent = $true + } + + { & $script:DiscoveryPath @Parameters } | Should -Throw '*new GA PowerShell lines: 7.7*' + $Report = Get-Content -LiteralPath $Parameters.OutputPath -Raw | ConvertFrom-Json + $Report.SupportContractReviewRequired | Should -BeTrue + $Report.NewLines[0].RequiredDecision | Should -Match 'CLR/TFM' + $Report.SupportContractMarker | Should -Be "" + } + + It 'does not treat a published preview with a future lifecycle start as a supported GA line' { + $Fixture = Get-SupportUpdateFixture -Root $TestDrive -ReleaseVersions @('7.4.18', '7.5.9', '7.6.4', '7.7.0') + $Report = & $script:DiscoveryPath -TestMatrixPath $Fixture.MatrixPath -ReleaseDataPath $Fixture.ReleasePath -LifecycleDataPath $Fixture.LifecyclePath -OutputPath (Join-Path $TestDrive 'future-line.json') -AsOfUtc '2026-08-08T00:00:00Z' + + @($Report.NewLines) | Should -HaveCount 0 + @($Report.UndeclaredSupportedLines) | Should -HaveCount 0 + $Report.SupportContractReviewRequired | Should -BeFalse + } + + It 'keeps a retirement-warning fingerprint stable while the remaining day count changes' { + $Fixture = Get-SupportUpdateFixture -Root $TestDrive -ReleaseVersions @('7.4.18', '7.5.9', '7.6.4') + $First = & $script:DiscoveryPath -TestMatrixPath $Fixture.MatrixPath -ReleaseDataPath $Fixture.ReleasePath -LifecycleDataPath $Fixture.LifecyclePath -OutputPath (Join-Path $TestDrive 'retirement-first.json') -AsOfUtc '2026-08-15T00:00:00Z' + $Second = & $script:DiscoveryPath -TestMatrixPath $Fixture.MatrixPath -ReleaseDataPath $Fixture.ReleasePath -LifecycleDataPath $Fixture.LifecyclePath -OutputPath (Join-Path $TestDrive 'retirement-second.json') -AsOfUtc '2026-08-16T00:00:00Z' + + $First.SupportContractReviewRequired | Should -BeTrue + @($First.Lifecycle | Where-Object Status -EQ 'RetiringSoon') | Should -HaveCount 2 + $First.SupportContractFingerprint | Should -BeExactly $Second.SupportContractFingerprint + } + + It 'marks a release line expired on the first unsupported Pacific date' { + $Fixture = Get-SupportUpdateFixture -Root $TestDrive -ReleaseVersions @('7.4.18', '7.5.9', '7.6.4') + $ReportPath = Join-Path $TestDrive 'first-unsupported-day.json' + $Parameters = @{ + TestMatrixPath = $Fixture.MatrixPath + ReleaseDataPath = $Fixture.ReleasePath + LifecycleDataPath = $Fixture.LifecyclePath + OutputPath = $ReportPath + AsOfUtc = '2026-11-11T08:00:00Z' + RequireCurrent = $true + } + + { & $script:DiscoveryPath @Parameters } | Should -Throw '*expired lines: 7.4, 7.5*' + $Report = Get-Content -LiteralPath $ReportPath -Raw | ConvertFrom-Json + $Expired = @($Report.Lifecycle | Where-Object Status -EQ 'Expired') + @($Expired.ReleaseLine) | Should -Be @('7.4', '7.5') + @($Expired.DaysRemaining | Sort-Object -Unique) | Should -Be @(0) + } +} diff --git a/tests/Unit/PowerShellTestMatrix.Tests.ps1 b/tests/Unit/PowerShellTestMatrix.Tests.ps1 new file mode 100644 index 00000000..d1a7d1c0 --- /dev/null +++ b/tests/Unit/PowerShellTestMatrix.Tests.ps1 @@ -0,0 +1,38 @@ +BeforeAll { + $script:RepositoryRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent + $script:GeneratorPath = Join-Path -Path $script:RepositoryRoot -ChildPath 'tools/New-DLLPicklePowerShellTestMatrix.ps1' +} + +Describe 'Generated exact PowerShell test matrix' -Tag 'Unit' { + It 'generates exactly three supported patches across three operating systems' { + $matrix = & $script:GeneratorPath -Compress | ConvertFrom-Json + + @($matrix.include) | Should -HaveCount 9 + @($matrix.include.powerShellVersion | Sort-Object -Unique) | Should -Be @('7.4.18', '7.5.9', '7.6.4') + @($matrix.include.platform | Sort-Object -Unique) | Should -Be @('linux', 'macos', 'windows') + @($matrix.include.targetFramework | Sort-Object -Unique) | Should -Be @('net10.0', 'net8.0', 'net9.0') + } + + It 'uses only exact versions and the default official archive provider' { + $matrix = & $script:GeneratorPath -Compress | ConvertFrom-Json + + @($matrix.include | Where-Object powerShellVersion -notmatch '^\d+\.\d+\.\d+$') | Should -BeNullOrEmpty + @($matrix.include.provider | Sort-Object -Unique) | Should -Be @('DirectArchive') + } + + It 'assigns one unique cell per version, platform, and architecture' { + $matrix = & $script:GeneratorPath -Compress | ConvertFrom-Json + $keys = @($matrix.include | ForEach-Object { '{0}|{1}|{2}' -f $_.powerShellVersion, $_.platform, $_.architecture }) + + @($keys | Sort-Object -Unique) | Should -HaveCount 9 + } + + It 'uses an Intel runner for the macOS x64 archive lane' { + $matrix = & $script:GeneratorPath -Compress | ConvertFrom-Json + $macosCells = @($matrix.include | Where-Object platform -eq 'macos') + + $macosCells | Should -HaveCount 3 + @($macosCells.architecture | Sort-Object -Unique) | Should -Be @('x64') + @($macosCells.runner | Sort-Object -Unique) | Should -Be @('macos-15-intel') + } +} diff --git a/tests/Unit/ProfileConflictBaseline.Tests.ps1 b/tests/Unit/ProfileConflictBaseline.Tests.ps1 new file mode 100644 index 00000000..d3b4901a --- /dev/null +++ b/tests/Unit/ProfileConflictBaseline.Tests.ps1 @@ -0,0 +1,280 @@ +BeforeAll { + $script:RepositoryRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent + $script:ToolPath = Join-Path -Path $script:RepositoryRoot -ChildPath 'tools/Test-DLLPickleProfileConflictBaseline.ps1' + + function Get-ProfileBaselineFixture { + param( + [string]$Status = 'accepted', + [string]$BaselineFingerprint = ('a' * 64), + [string]$CurrentFingerprint = ('a' * 64), + [string]$BaselineScenarioFingerprint = ('b' * 64), + [string]$CurrentScenarioFingerprint = ('b' * 64) + ) + + $root = Join-Path -Path $TestDrive -ChildPath ([guid]::NewGuid().ToString('n')) + $null = New-Item -Path $root -ItemType Directory -Force + $policyPath = Join-Path $root 'policy.json' + $matrixPath = Join-Path $root 'matrix.json' + $scenarioPath = Join-Path $root 'scenario.json' + $normalizedPath = Join-Path $root 'candidate-evidence.json' + $committedEvidencePath = Join-Path $root 'accepted-evidence.json' + + $EvidenceContent = [ordered]@{ + profile = [ordered]@{ profileKey = 'ps7.6-net10.0-windows-x64' } + validation = [ordered]@{ + deterministicImportNoAuth = [ordered]@{ + status = 'passed' + writesPerformed = $false + conflictSurfaceFingerprint = $CurrentFingerprint + scenarioFingerprint = $CurrentScenarioFingerprint + } + authenticatedReadOnly = [ordered]@{ + status = 'not-run-no-approved-credentials' + writesPerformed = $false + } + } + marker = 'stable-evidence' + } + $CanonicalEvidence = $EvidenceContent | ConvertTo-Json -Depth 100 -Compress + $EvidenceBytes = [System.Text.Encoding]::UTF8.GetBytes($CanonicalEvidence) + $EvidenceFingerprint = [System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::HashData($EvidenceBytes)).Replace('-', '').ToLowerInvariant() + $Evidence = [ordered]@{ + schemaVersion = 1 + contentFingerprint = $EvidenceFingerprint + provenance = [ordered]@{ sourceRunId = 'fixture' } + content = $EvidenceContent + } + $Evidence | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $normalizedPath -Encoding utf8 + $Evidence | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $committedEvidencePath -Encoding utf8 + + [ordered]@{ + runtimeProfiles = @( + [ordered]@{ + powerShellLine = '7.6' + targetFramework = 'net10.0' + baselines = [ordered]@{ + windows = [ordered]@{ + status = $Status + conflictSurfaceFingerprint = $BaselineFingerprint + scenarioFingerprint = $BaselineScenarioFingerprint + evidencePath = 'accepted-evidence.json' + evidenceFingerprint = $EvidenceFingerprint + } + } + } + ) + } | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $policyPath -Encoding utf8 + [ordered]@{ + ProfileKey = 'ps7.6-net10.0-windows-x64' + Profile = [ordered]@{ + PowerShellLine = '7.6' + TargetFramework = 'net10.0' + Platform = 'windows' + Architecture = 'x64' + } + Fingerprint = $CurrentFingerprint + } | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $matrixPath -Encoding utf8 + + [ordered]@{ + ProfileKey = 'ps7.6-net10.0-windows-x64' + Profile = [ordered]@{ + PowerShellLine = '7.6' + TargetFramework = 'net10.0' + Platform = 'windows' + Architecture = 'x64' + } + ValidationTier = 'deterministic-import-no-auth' + WritesPerformed = $false + Passed = $true + ScenarioFingerprint = $CurrentScenarioFingerprint + Scenarios = @( + [ordered]@{ + Assemblies = @( + [ordered]@{ + Name = 'Microsoft.Identity.Client' + Platform = 'windows' + Architecture = 'x64' + } + ) + } + ) + } | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $scenarioPath -Encoding utf8 + + [pscustomobject]@{ + PolicyPath = $policyPath + MatrixPath = $matrixPath + ScenarioPath = $scenarioPath + NormalizedPath = $normalizedPath + CommittedEvidencePath = $committedEvidencePath + } + } +} + +Describe 'Profile-specific conflict baseline enforcement' -Tag 'Unit' { + It 'passes only an accepted unchanged exact-profile baseline' { + $fixture = Get-ProfileBaselineFixture + + $result = & $script:ToolPath -PolicyPath $fixture.PolicyPath -ConflictMatrixPath $fixture.MatrixPath -ScenarioEvidencePath $fixture.ScenarioPath -NormalizedEvidencePath $fixture.NormalizedPath -PassThru + + $result.Status | Should -Be 'AcceptedUnchanged' + $result.ProfileKey | Should -Be 'ps7.6-net10.0-windows-x64' + } + + It 'fails closed when profile evidence has not been accepted' { + $fixture = Get-ProfileBaselineFixture -Status 'requires-profile-refresh' -BaselineFingerprint $null + + { & $script:ToolPath -PolicyPath $fixture.PolicyPath -ConflictMatrixPath $fixture.MatrixPath -ScenarioEvidencePath $fixture.ScenarioPath -NormalizedEvidencePath $fixture.NormalizedPath } | + Should -Throw '*not accepted*requires-profile-refresh*' + } + + It 'fails closed when the accepted profile fingerprint drifts' { + $fixture = Get-ProfileBaselineFixture -CurrentFingerprint ('b' * 64) + + { & $script:ToolPath -PolicyPath $fixture.PolicyPath -ConflictMatrixPath $fixture.MatrixPath -ScenarioEvidencePath $fixture.ScenarioPath -NormalizedEvidencePath $fixture.NormalizedPath } | + Should -Throw '*drift*baseline*current*' + } + + It 'fails closed when deterministic import-order evidence drifts' { + $fixture = Get-ProfileBaselineFixture -CurrentScenarioFingerprint ('c' * 64) + + { & $script:ToolPath -PolicyPath $fixture.PolicyPath -ConflictMatrixPath $fixture.MatrixPath -ScenarioEvidencePath $fixture.ScenarioPath -NormalizedEvidencePath $fixture.NormalizedPath } | + Should -Throw '*profile evidence drift*' + } + + It 'fails closed when normalized evidence content drifts independently' { + $fixture = Get-ProfileBaselineFixture + $Candidate = Get-Content -LiteralPath $fixture.NormalizedPath -Raw | ConvertFrom-Json + $Candidate.content.marker = 'changed-evidence' + $CanonicalContent = $Candidate.content | ConvertTo-Json -Depth 100 -Compress + $Bytes = [System.Text.Encoding]::UTF8.GetBytes($CanonicalContent) + $Candidate.contentFingerprint = [System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::HashData($Bytes)).Replace('-', '').ToLowerInvariant() + $Candidate | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $fixture.NormalizedPath -Encoding UTF8 + + { & $script:ToolPath -PolicyPath $fixture.PolicyPath -ConflictMatrixPath $fixture.MatrixPath -ScenarioEvidencePath $fixture.ScenarioPath -NormalizedEvidencePath $fixture.NormalizedPath } | + Should -Throw '*profile evidence drift*baseline evidence*current evidence*' + } + + It 'rejects normalized candidate evidence whose content fingerprint was tampered' { + $fixture = Get-ProfileBaselineFixture + $Candidate = Get-Content -LiteralPath $fixture.NormalizedPath -Raw | ConvertFrom-Json + $Candidate.content.marker = 'tampered-without-rehash' + $Candidate | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $fixture.NormalizedPath -Encoding UTF8 + + { & $script:ToolPath -PolicyPath $fixture.PolicyPath -ConflictMatrixPath $fixture.MatrixPath -ScenarioEvidencePath $fixture.ScenarioPath -NormalizedEvidencePath $fixture.NormalizedPath } | + Should -Throw '*does not recompute*' + } + + It 'rejects a tampered committed snapshot even when the candidate is unchanged' { + $fixture = Get-ProfileBaselineFixture + $OutputPath = Join-Path $TestDrive 'invalid-committed-comparison.json' + $Committed = Get-Content -LiteralPath $fixture.CommittedEvidencePath -Raw | ConvertFrom-Json + $Committed.content.marker = 'tampered-committed-evidence' + $Committed | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $fixture.CommittedEvidencePath -Encoding UTF8 + + { & $script:ToolPath -PolicyPath $fixture.PolicyPath -ConflictMatrixPath $fixture.MatrixPath -ScenarioEvidencePath $fixture.ScenarioPath -NormalizedEvidencePath $fixture.NormalizedPath -OutputPath $OutputPath } | + Should -Throw '*accepted evidence*does not recompute*policy fingerprint*' + $Result = Get-Content -LiteralPath $OutputPath -Raw | ConvertFrom-Json + $Result.Status | Should -Be 'InvalidCommittedEvidence' + $Result.FailureDetail | Should -Match 'does not recompute to the policy fingerprint' + $Result.FindingFingerprint | Should -Match '^[a-f0-9]{64}$' + } + + It 'writes a structured result before a missing accepted snapshot fails closed' { + $fixture = Get-ProfileBaselineFixture + $OutputPath = Join-Path $TestDrive 'missing-committed-comparison.json' + Remove-Item -LiteralPath $fixture.CommittedEvidencePath + + { & $script:ToolPath -PolicyPath $fixture.PolicyPath -ConflictMatrixPath $fixture.MatrixPath -ScenarioEvidencePath $fixture.ScenarioPath -NormalizedEvidencePath $fixture.NormalizedPath -OutputPath $OutputPath } | + Should -Throw '*accepted evidence*was not found*' + $Result = Get-Content -LiteralPath $OutputPath -Raw | ConvertFrom-Json + $Result.Status | Should -Be 'InvalidCommittedEvidence' + $Result.FailureDetail | Should -Match 'was not found' + $Result.FindingFingerprint | Should -Match '^[a-f0-9]{64}$' + } + + It 'rejects a conflict matrix whose key does not match its profile fields' { + $fixture = Get-ProfileBaselineFixture + $Matrix = Get-Content -LiteralPath $fixture.MatrixPath -Raw | ConvertFrom-Json + $Matrix.ProfileKey = 'ps7.6-net10.0-linux-x64' + $Matrix | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $fixture.MatrixPath -Encoding UTF8 + + { & $script:ToolPath -PolicyPath $fixture.PolicyPath -ConflictMatrixPath $fixture.MatrixPath -ScenarioEvidencePath $fixture.ScenarioPath -NormalizedEvidencePath $fixture.NormalizedPath } | + Should -Throw '*does not match derived profile key*' + } + + It 'rejects scenario metadata whose key does not match its profile fields' { + $fixture = Get-ProfileBaselineFixture + $Scenario = Get-Content -LiteralPath $fixture.ScenarioPath -Raw | ConvertFrom-Json + $Scenario.Profile.Platform = 'linux' + $Scenario | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $fixture.ScenarioPath -Encoding UTF8 + + { & $script:ToolPath -PolicyPath $fixture.PolicyPath -ConflictMatrixPath $fixture.MatrixPath -ScenarioEvidencePath $fixture.ScenarioPath -NormalizedEvidencePath $fixture.NormalizedPath } | + Should -Throw '*does not match derived profile key*' + } + + It 'rejects scenario rows observed on a different platform or architecture' { + $fixture = Get-ProfileBaselineFixture + $Scenario = Get-Content -LiteralPath $fixture.ScenarioPath -Raw | ConvertFrom-Json + $Scenario.Scenarios[0].Assemblies[0].Platform = 'linux' + $Scenario.Scenarios[0].Assemblies[0].Architecture = 'arm64' + $Scenario | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $fixture.ScenarioPath -Encoding UTF8 + + { & $script:ToolPath -PolicyPath $fixture.PolicyPath -ConflictMatrixPath $fixture.MatrixPath -ScenarioEvidencePath $fixture.ScenarioPath -NormalizedEvidencePath $fixture.NormalizedPath } | + Should -Throw '*observed on*linux/arm64*expected*windows/x64*' + } + + It 'writes a stable finding before an unaccepted baseline fails closed' { + $fixture = Get-ProfileBaselineFixture -Status 'requires-profile-refresh' -BaselineFingerprint $null + $OutputPath = Join-Path $TestDrive 'baseline-comparison.json' + + $Parameters = @{ + PolicyPath = $fixture.PolicyPath + ConflictMatrixPath = $fixture.MatrixPath + ScenarioEvidencePath = $fixture.ScenarioPath + NormalizedEvidencePath = $fixture.NormalizedPath + OutputPath = $OutputPath + } + { & $script:ToolPath @Parameters } | Should -Throw '*not accepted*' + $Result = Get-Content -LiteralPath $OutputPath -Raw | ConvertFrom-Json + $Result.Status | Should -Be 'RequiresAcceptance' + $Result.FindingFingerprint | Should -Match '^[a-f0-9]{64}$' + } +} + +Describe 'Finding fingerprint report deduplication' -Tag 'Unit' { + It 'matches only the exact stable marker' { + $ScriptPath = Join-Path $script:RepositoryRoot 'tools\Test-DLLPickleFindingFingerprintReported.ps1' + $Fingerprint = 'a' * 64 + & $ScriptPath -Fingerprint $Fingerprint -Text @('ordinary text', "") | Should -BeTrue + & $ScriptPath -Fingerprint $Fingerprint -Text @('ordinary text', '') | Should -BeFalse + } + + It 'aggregates profile findings into one deterministic marker' { + $SummaryScriptPath = Join-Path $script:RepositoryRoot 'tools\New-DLLPickleProfileEvidenceSummary.ps1' + $MatrixPath = Join-Path $TestDrive 'summary-matrix.json' + $EvidenceRoot = Join-Path $TestDrive 'summary-evidence' + $null = New-Item -Path $EvidenceRoot -ItemType Directory + @{ + profiles = @(@{ powerShellMajor = 7; powerShellMinor = 6; targetFramework = 'net10.0' }) + lanes = @(@{ platform = 'windows'; architecture = 'x64' }) + } | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath $MatrixPath -Encoding UTF8 + @{ + ProfileKey = 'ps7.6-net10.0-windows-x64' + Status = 'RequiresAcceptance' + BaselineStatus = 'requires-profile-refresh' + BaselineFingerprint = $null + CurrentFingerprint = 'b' * 64 + BaselineEvidenceFingerprint = $null + CurrentEvidenceFingerprint = 'd' * 64 + FindingFingerprint = 'c' * 64 + } | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $EvidenceRoot 'baseline-comparison.json') -Encoding UTF8 + + $First = & $SummaryScriptPath -EvidenceRoot $EvidenceRoot -TestMatrixPath $MatrixPath -OutputPath (Join-Path $TestDrive 'first-summary.json') + $Second = & $SummaryScriptPath -EvidenceRoot $EvidenceRoot -TestMatrixPath $MatrixPath -OutputPath (Join-Path $TestDrive 'second-summary.json') + + $First.AllProfileEvidencePresent | Should -BeTrue + $First.ReadyForCandidateUpdate | Should -BeFalse + $First.AggregateFindingFingerprint | Should -BeExactly $Second.AggregateFindingFingerprint + $First.FindingMarker | Should -Be "" + } +} diff --git a/tests/Unit/ProfileEvidenceHelpers.Tests.ps1 b/tests/Unit/ProfileEvidenceHelpers.Tests.ps1 new file mode 100644 index 00000000..5ed1160d --- /dev/null +++ b/tests/Unit/ProfileEvidenceHelpers.Tests.ps1 @@ -0,0 +1,47 @@ +BeforeAll { + $RepositoryRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent + . (Join-Path $RepositoryRoot 'tools\DLLPickle.ProfileEvidence.ps1') +} + +Describe 'Profile evidence helpers' -Tag 'Unit' { + It 'recomputes the canonical UTF-8 SHA-256 content fingerprint' { + $Evidence = [pscustomobject]@{ + schemaVersion = 1 + content = [ordered]@{ + profile = 'ps7.6-net10.0-windows-x64' + values = @('z', 'a') + } + } + $CanonicalContent = $Evidence.content | ConvertTo-Json -Depth 100 -Compress + $Bytes = [System.Text.Encoding]::UTF8.GetBytes($CanonicalContent) + $Expected = [System.BitConverter]::ToString( + [System.Security.Cryptography.SHA256]::HashData($Bytes) + ).Replace('-', '').ToLowerInvariant() + + Get-DLLPickleNormalizedEvidenceFingerprint -Evidence $Evidence | Should -BeExactly $Expected + } + + It 'rejects unsupported or content-free evidence envelopes' { + { Get-DLLPickleNormalizedEvidenceFingerprint -Evidence ([pscustomobject]@{ schemaVersion = 2; content = @{} }) } | + Should -Throw '*unsupported schema*' + { Get-DLLPickleNormalizedEvidenceFingerprint -Evidence ([pscustomobject]@{ schemaVersion = 1 }) } | + Should -Throw '*no fingerprinted content*' + } + + It 'normalizes date/time inputs to UTC with invariant parsing' { + (ConvertTo-DLLPickleUtcDateTimeOffset -Value '2026-08-09T08:00:00-04:00').ToString('o') | + Should -BeExactly '2026-08-09T12:00:00.0000000+00:00' + } + + It 'uses ordinal ordering and uniqueness regardless of process culture' { + $OriginalCulture = [System.Globalization.CultureInfo]::CurrentCulture + try { + [System.Globalization.CultureInfo]::CurrentCulture = [System.Globalization.CultureInfo]::GetCultureInfo('tr-TR') + $Actual = @(Get-DLLPickleOrdinalSequence -InputObject @('z', $null, 'a', 'A', 'z') -Unique) + } finally { + [System.Globalization.CultureInfo]::CurrentCulture = $OriginalCulture + } + + ($Actual -join ',') | Should -BeExactly 'A,a,z' + } +} diff --git a/tests/Unit/RuntimeAssemblyProbe.Tests.ps1 b/tests/Unit/RuntimeAssemblyProbe.Tests.ps1 index 7b9918f0..90ba7f35 100644 --- a/tests/Unit/RuntimeAssemblyProbe.Tests.ps1 +++ b/tests/Unit/RuntimeAssemblyProbe.Tests.ps1 @@ -22,6 +22,22 @@ Describe 'Get-DLLPickleLoadedTrackedAssembly' -Tag 'Unit' { $Row | Should -Not -BeNullOrEmpty $Row.Alc | Should -Not -BeNullOrEmpty $Row.Version | Should -Not -BeNullOrEmpty + $Row.Platform | Should -BeIn @('windows', 'linux', 'macos') + $Row.OS | Should -Not -BeNullOrEmpty + if ($Row.Platform -eq 'macos') { + $Row.OS | Should -Match '^macOS \d+\.\d+' + } + } + + It 'uses the stable macOS product version instead of the Darwin kernel description' { + $Source = Get-Content -LiteralPath $LoadedScript -Raw + + $Source | Should -Match ([regex]::Escape('/usr/bin/sw_vers -productVersion')) + $Source | Should -Match ([regex]::Escape('$OperatingSystemDescription = "macOS $MacOSProductVersion"')) + Get-Content -LiteralPath (Join-Path $RepoRoot 'build\profile-evidence\ps7.4-net8.0-macos-x64.json') -Raw | + Should -Not -Match 'Darwin Kernel Version' + Get-Content -LiteralPath (Join-Path $RepoRoot 'build\profile-evidence\ps7.5-net9.0-macos-x64.json') -Raw | + Should -Not -Match 'Darwin Kernel Version' } It 'excludes loaded assemblies that are not in trackedAssemblies' { @@ -47,21 +63,87 @@ Describe 'Get-DLLPickleRuntimeAssemblySnapshot' -Tag 'Unit' { It 'sources its filter from -PolicyPath and returns tracked assemblies loaded in the child session' { $Policy = Get-TempPolicyPath -TrackedAssemblies @('System.Management.Automation') # Microsoft.PowerShell.Management is always importable; the child always has SMA loaded. - $Result = & $SnapshotScript -ModuleName 'Microsoft.PowerShell.Management' -PolicyPath $Policy + $Result = & $SnapshotScript -ModuleName 'Microsoft.PowerShell.Management' -PolicyPath $Policy -PowerShellExecutable ([Environment]::ProcessPath) -PowerShellVersion $PSVersionTable.PSVersion -TargetFramework ('net{0}.0' -f [Environment]::Version.Major) ($Result | Where-Object Name -EQ 'System.Management.Automation') | Should -Not -BeNullOrEmpty + $Result[0].PowerShellVersion | Should -Be $PSVersionTable.PSVersion.ToString() + $Result[0].TargetFramework | Should -Be ('net{0}.0' -f [Environment]::Version.Major) + $Result[0].ExecutablePath | Should -Not -BeNullOrEmpty + $Result[0].Platform | Should -BeIn @('windows', 'linux', 'macos') + $Result[0].Architecture | Should -Not -BeNullOrEmpty } It 'throws in strict mode when a module cannot be imported' { $Policy = Get-TempPolicyPath -TrackedAssemblies @('System.Management.Automation') - { & $SnapshotScript -ModuleName 'DLLPickle.DefinitelyMissing' -PolicyPath $Policy -Strict } | + { & $SnapshotScript -ModuleName 'DLLPickle.DefinitelyMissing' -PolicyPath $Policy -PowerShellExecutable ([Environment]::ProcessPath) -Strict } | Should -Throw '*runtime assembly snapshot failed*' } It 'throws in strict mode when the probe command fails' { $Policy = Get-TempPolicyPath -TrackedAssemblies @('System.Management.Automation') - { & $SnapshotScript -ModuleName 'Microsoft.PowerShell.Management' -PolicyPath $Policy -ProbeCommand "throw 'probe failed'" -Strict } | + { & $SnapshotScript -ModuleName 'Microsoft.PowerShell.Management' -PolicyPath $Policy -PowerShellExecutable ([Environment]::ProcessPath) -ProbeCommand "throw 'probe failed'" -Strict } | Should -Throw '*runtime assembly snapshot failed*' } + + It 'throws in strict mode when DLLPickle reports a failed preload row' { + $Policy = Get-TempPolicyPath -TrackedAssemblies @('System.Management.Automation') + $DllPickleRoot = Join-Path $TestDrive 'failing-dllpickle' + $null = New-Item -Path $DllPickleRoot -ItemType Directory + @' +function Import-DPLibrary { + [CmdletBinding()] + param([switch]$SuppressLogo) + + [pscustomobject]@{ Status = 'Failed' } +} +'@ | Set-Content -LiteralPath (Join-Path $DllPickleRoot 'DLLPickle.psm1') -Encoding UTF8 + $DllPickleManifest = Join-Path $DllPickleRoot 'DLLPickle.psd1' + New-ModuleManifest -Path $DllPickleManifest -RootModule 'DLLPickle.psm1' -ModuleVersion '1.0.0' -FunctionsToExport @('Import-DPLibrary') + + { & $SnapshotScript -ModuleName 'Microsoft.PowerShell.Management' -PreloadDllPickleManifest $DllPickleManifest -PolicyPath $Policy -PowerShellExecutable ([Environment]::ProcessPath) -Strict } | + Should -Throw '*DLLPickle preload reported 1 failed assembly load*' + } + + It 'imports an exact manifest under an explicitly isolated module path' { + $Policy = Get-TempPolicyPath -TrackedAssemblies @('System.Management.Automation') + $ModuleRoot = Join-Path $TestDrive 'isolated-module' + $null = New-Item -Path $ModuleRoot -ItemType Directory + Set-Content -LiteralPath (Join-Path $ModuleRoot 'Synthetic.Isolated.psm1') -Value '# isolated import target' -Encoding UTF8 + $ManifestPath = Join-Path $ModuleRoot 'Synthetic.Isolated.psd1' + New-ModuleManifest -Path $ManifestPath -RootModule 'Synthetic.Isolated.psm1' -ModuleVersion '1.0.0' + + $Result = & $SnapshotScript -ModuleName 'Synthetic.Isolated' -ModuleManifestPath $ManifestPath -ModuleSearchPath @($ModuleRoot, (Join-Path $PSHOME 'Modules')) -PolicyPath $Policy -PowerShellExecutable ([Environment]::ProcessPath) -Strict + + $Result[0].ImportedModulePaths | Should -Contain $ManifestPath + $Result[0].IsolatedModulePath | Should -Be (@($ModuleRoot, (Join-Path $PSHOME 'Modules')) -join [System.IO.Path]::PathSeparator) + } + + It 'keeps module informational output separate from the JSON result' { + $Policy = Get-TempPolicyPath -TrackedAssemblies @('System.Management.Automation') + $ModuleRoot = Join-Path $TestDrive 'noisy-module' + $null = New-Item -Path $ModuleRoot -ItemType Directory + Set-Content -LiteralPath (Join-Path $ModuleRoot 'Synthetic.Noisy.psm1') -Value "Write-Host 'Get started with Synthetic.Noisy'" -Encoding UTF8 + $ManifestPath = Join-Path $ModuleRoot 'Synthetic.Noisy.psd1' + New-ModuleManifest -Path $ManifestPath -RootModule 'Synthetic.Noisy.psm1' -ModuleVersion '1.0.0' + + $Result = & $SnapshotScript -ModuleName 'Synthetic.Noisy' -ModuleManifestPath $ManifestPath -ModuleSearchPath @($ModuleRoot, (Join-Path $PSHOME 'Modules')) -PolicyPath $Policy -PowerShellExecutable ([Environment]::ProcessPath) -Strict + + ($Result | Where-Object Name -EQ 'System.Management.Automation') | Should -Not -BeNullOrEmpty + } + + It 'returns an empty snapshot when no tracked assemblies are loaded' { + $Policy = Get-TempPolicyPath -TrackedAssemblies @('DLLPickle.NotLoaded') + + $Result = @(& $SnapshotScript -ModuleName 'Microsoft.PowerShell.Management' -PolicyPath $Policy -PowerShellExecutable ([Environment]::ProcessPath) -Strict) + + $Result | Should -HaveCount 0 + } + + It 'never launches a generic pwsh command from PATH' { + $Source = Get-Content -LiteralPath $SnapshotScript -Raw + + $Source | Should -Match 'PowerShellExecutable' + $Source | Should -Not -Match '(?m)&\s+pwsh\b' + } } diff --git a/tests/Unit/RuntimeProfileEvidence.Tests.ps1 b/tests/Unit/RuntimeProfileEvidence.Tests.ps1 new file mode 100644 index 00000000..426ad691 --- /dev/null +++ b/tests/Unit/RuntimeProfileEvidence.Tests.ps1 @@ -0,0 +1,12 @@ +BeforeAll { + $script:ToolPath = Join-Path $PSScriptRoot '..\..\tools\New-DLLPickleRuntimeProfileEvidence.ps1' +} + +Describe 'Runtime profile evidence compatibility' -Tag 'Unit' { + It 'imports an absolute manifest path with the cross-version Name parameter' { + $Source = Get-Content -LiteralPath $script:ToolPath -Raw + + $Source | Should -Match ([regex]::Escape('Import-Module -Name $Payload.manifestPath -Force')) + $Source | Should -Not -Match ([regex]::Escape('Import-Module -LiteralPath $Payload.manifestPath')) + } +} diff --git a/tests/Unit/RuntimeProfilePolicy.Tests.ps1 b/tests/Unit/RuntimeProfilePolicy.Tests.ps1 new file mode 100644 index 00000000..b5fdb88f --- /dev/null +++ b/tests/Unit/RuntimeProfilePolicy.Tests.ps1 @@ -0,0 +1,182 @@ +BeforeAll { + $script:RepositoryRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent + $script:RuntimePolicyPath = Join-Path -Path $script:RepositoryRoot -ChildPath 'src/DLLPickle/SupportedRuntimeProfiles.json' + $script:TestMatrixPath = Join-Path -Path $script:RepositoryRoot -ChildPath 'build/powershell-test-matrix.json' + $script:PolicyTestScriptPath = Join-Path -Path $script:RepositoryRoot -ChildPath 'tools/Test-DLLPickleRuntimeProfilePolicy.ps1' +} + +Describe 'Runtime profile policy data' -Tag 'Unit' { + It 'declares only runtime behavior in the shipped policy' { + $script:RuntimePolicyPath | Should -Exist + $rawPolicy = Get-Content -LiteralPath $script:RuntimePolicyPath -Raw + $policy = $rawPolicy | ConvertFrom-Json + + $policy.schemaVersion | Should -Be 1 + @($policy.profiles) | Should -HaveCount 3 + $rawPolicy | Should -Not -Match '7\.4\.18|7\.5\.9|7\.6\.4|multi-pwsh|MultiPwsh|lifecycle|checksum|sha256' + } + + It 'maps every supported PowerShell line to one CLR major and TFM' { + $policy = Get-Content -LiteralPath $script:RuntimePolicyPath -Raw | ConvertFrom-Json + $actual = @($policy.profiles | ForEach-Object { + '{0}.{1}|{2}|{3}' -f $_.powerShellMajor, $_.powerShellMinor, $_.dotnetMajor, $_.targetFramework + }) + + $actual | Should -Be @( + '7.4|8|net8.0' + '7.5|9|net9.0' + '7.6|10|net10.0' + ) + @($actual | Sort-Object -Unique) | Should -HaveCount $actual.Count + } + + It 'declares platform-provided assemblies for the universal module payload' { + $policy = Get-Content -LiteralPath $script:RuntimePolicyPath -Raw | ConvertFrom-Json + + foreach ($RuntimeProfileRow in @($policy.profiles)) { + @($RuntimeProfileRow.hostProvidedAssemblyNames.windows) | Should -Be @('System.Security.Cryptography.ProtectedData') + @($RuntimeProfileRow.hostProvidedAssemblyNames.linux) | Should -BeNullOrEmpty + @($RuntimeProfileRow.hostProvidedAssemblyNames.macos) | Should -BeNullOrEmpty + } + } + + It 'keeps exact patch, lifecycle, stock archive, and optional tool metadata outside the package' { + $script:TestMatrixPath | Should -Exist + $rawMatrix = Get-Content -LiteralPath $script:TestMatrixPath -Raw + $matrix = $rawMatrix | ConvertFrom-Json + + $matrix.schemaVersion | Should -Be 1 + $rawMatrix | Should -Match '"lastVerifiedUtc":\s*"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z"' + $matrix.evidenceFreshnessDays | Should -BeGreaterThan 0 + $matrix.retirementWarningDays | Should -BeGreaterThan 0 + @($matrix.profiles).powerShellVersion | Should -Be @('7.4.18', '7.5.9', '7.6.4') + @($matrix.lanes).platform | Should -Be @('windows', 'linux', 'macos') + @($matrix.archiveAssets) | Should -HaveCount 9 + $matrix.provisioning.optionalProvider.name | Should -Be 'MultiPwsh' + $matrix.provisioning.optionalProvider.version | Should -Be '0.18.0' + } + + It 'aligns the shipped and CI profile sets exactly' { + $policy = Get-Content -LiteralPath $script:RuntimePolicyPath -Raw | ConvertFrom-Json + $matrix = Get-Content -LiteralPath $script:TestMatrixPath -Raw | ConvertFrom-Json + $shipped = @($policy.profiles | ForEach-Object { + '{0}.{1}|{2}|{3}' -f $_.powerShellMajor, $_.powerShellMinor, $_.dotnetMajor, $_.targetFramework + }) + $tested = @($matrix.profiles | ForEach-Object { + '{0}.{1}|{2}|{3}' -f $_.powerShellMajor, $_.powerShellMinor, $_.dotnetMajor, $_.targetFramework + }) + + $tested | Should -Be $shipped + } + + It 'pins one checksum-verified official stock archive for every profile and lane' { + $matrix = Get-Content -LiteralPath $script:TestMatrixPath -Raw | ConvertFrom-Json + foreach ($RuntimeProfile in @($matrix.profiles)) { + foreach ($lane in @($matrix.lanes)) { + $MatchingAssets = @($matrix.archiveAssets | Where-Object { + $_.powerShellVersion -eq $RuntimeProfile.powerShellVersion -and + $_.platform -eq $lane.platform -and + $_.architecture -eq $lane.architecture + }) + $MatchingAssets | Should -HaveCount 1 + $MatchingAssets[0].downloadUrl | Should -Match '^https://github\.com/PowerShell/PowerShell/releases/download/v' + $MatchingAssets[0].sha256 | Should -Match '^[a-f0-9]{64}$' + } + } + } + + It 'pins checksum-verified multi-pwsh assets only for the optional CI provider' { + $matrix = Get-Content -LiteralPath $script:TestMatrixPath -Raw | ConvertFrom-Json + $provider = $matrix.provisioning.optionalProvider + + $provider.releaseUrl | Should -Be 'https://github.com/Devolutions/multi-pwsh/releases/tag/v0.18.0' + @($provider.assets) | Should -HaveCount 3 + foreach ($asset in @($provider.assets)) { + $asset.sha256 | Should -Match '^[a-f0-9]{64}$' + $asset.downloadUrl | Should -Match '/Devolutions/multi-pwsh/releases/download/v0\.18\.0/' + } + } +} + +Describe 'Runtime profile lifecycle enforcement' -Tag 'Unit' { + It 'passes a release check while policy evidence is fresh and every line is supported' { + $script:PolicyTestScriptPath | Should -Exist + + $result = @(& $script:PolicyTestScriptPath -Mode Release -AsOfUtc ([datetime]'2026-08-09T01:11:47Z') -PassThru) + + $result | Should -HaveCount 3 + @($result.Status | Sort-Object -Unique) | Should -Be @('Supported') + } + + It 'fails closed when a shipped support line is expired' { + { + & $script:PolicyTestScriptPath -Mode Release -AsOfUtc ([datetime]'2026-11-11T08:00:00Z') + } | Should -Throw '*expired*7.4*7.5*' + } + + It 'keeps a line supported through the end of its Pacific lifecycle day' { + $result = @(& $script:PolicyTestScriptPath -Mode Scheduled -AsOfUtc ([datetime]'2026-11-11T07:59:59Z') -PassThru -WarningAction SilentlyContinue) + + @($result | Where-Object Status -EQ 'Expired') | Should -BeNullOrEmpty + } + + It 'warns on schedule before an impending retirement' { + $warnings = @() + + $result = @(& $script:PolicyTestScriptPath -Mode Scheduled -AsOfUtc ([datetime]'2026-08-15T00:00:00Z') -PassThru -WarningVariable warnings) + + $warnings | Should -Not -BeNullOrEmpty + @($result | Where-Object Status -eq 'RetiringSoon') | Should -HaveCount 2 + } + + It 'fails a release check when lifecycle verification is stale' { + { + & $script:PolicyTestScriptPath -Mode Release -AsOfUtc ([datetime]'2026-09-15T00:00:00Z') + } | Should -Throw '*stale*last verified*' + } + + It 'uses a release-current live discovery report as fresh lifecycle evidence' { + $EvidencePath = Join-Path $TestDrive 'live-lifecycle-evidence.json' + @{ + schemaVersion = 1 + generatedAtUtc = '2026-09-15T00:00:00Z' + patchUpdates = @() + newLines = @() + lifecycle = @( + @{ releaseLine = '7.4'; status = 'Supported' } + @{ releaseLine = '7.5'; status = 'Supported' } + @{ releaseLine = '7.6'; status = 'Supported' } + ) + lifecycleDateChanges = @() + lifecycleMissingLines = @() + undeclaredSupportedLines = @() + supportContractReviewRequired = $false + } | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $EvidencePath -Encoding utf8 + + $result = @(& $script:PolicyTestScriptPath -Mode Release -AsOfUtc ([datetime]'2026-09-15T00:01:00Z') -LifecycleEvidencePath $EvidencePath -PassThru) + + $result | Should -HaveCount 3 + } + + It 'rejects live discovery evidence with an outstanding support update' { + $EvidencePath = Join-Path $TestDrive 'outdated-lifecycle-evidence.json' + @{ + schemaVersion = 1 + generatedAtUtc = '2026-09-15T00:00:00Z' + patchUpdates = @(@{ candidateVersion = '7.5.10' }) + newLines = @() + lifecycle = @( + @{ releaseLine = '7.4'; status = 'Supported' } + @{ releaseLine = '7.5'; status = 'Supported' } + @{ releaseLine = '7.6'; status = 'Supported' } + ) + lifecycleDateChanges = @() + lifecycleMissingLines = @() + undeclaredSupportedLines = @() + supportContractReviewRequired = $false + } | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $EvidencePath -Encoding utf8 + + { & $script:PolicyTestScriptPath -Mode Release -AsOfUtc ([datetime]'2026-09-15T00:01:00Z') -LifecycleEvidencePath $EvidencePath } | + Should -Throw '*not release-current*newer servicing patches*' + } +} diff --git a/tests/Unit/RuntimeProfileSelection.Tests.ps1 b/tests/Unit/RuntimeProfileSelection.Tests.ps1 new file mode 100644 index 00000000..5b1e3432 --- /dev/null +++ b/tests/Unit/RuntimeProfileSelection.Tests.ps1 @@ -0,0 +1,72 @@ +BeforeAll { + $script:RepositoryRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent + $script:RuntimePolicyPath = Join-Path -Path $script:RepositoryRoot -ChildPath 'src/DLLPickle/SupportedRuntimeProfiles.json' + . (Join-Path -Path $script:RepositoryRoot -ChildPath 'src/DLLPickle/Private/Get-DPRuntimeProfile.ps1') +} + +Describe 'Get-DPRuntimeProfile' -Tag 'Unit' { + It 'selects for PowerShell on CLR ' -ForEach @( + @{ PowerShellVersion = [version]'7.4.18'; DotNetMajor = 8; TargetFramework = 'net8.0' } + @{ PowerShellVersion = [version]'7.5.9'; DotNetMajor = 9; TargetFramework = 'net9.0' } + @{ PowerShellVersion = [version]'7.6.4'; DotNetMajor = 10; TargetFramework = 'net10.0' } + ) { + $result = Get-DPRuntimeProfile -PolicyPath $script:RuntimePolicyPath -PowerShellVersion $PowerShellVersion -DotNetMajor $DotNetMajor + + $result.targetFramework | Should -Be $TargetFramework + @($result.hostProvidedAssemblyNames.windows) | Should -Be @('System.Security.Cryptography.ProtectedData') + } + + It 'fails closed for an unsupported PowerShell line' { + { + Get-DPRuntimeProfile -PolicyPath $script:RuntimePolicyPath -PowerShellVersion ([version]'7.7.0') -DotNetMajor 10 + } | Should -Throw '*Unsupported PowerShell runtime*7.4*7.5*7.6*' + } + + It 'fails closed when the PowerShell line is hosted on the wrong CLR major' { + { + Get-DPRuntimeProfile -PolicyPath $script:RuntimePolicyPath -PowerShellVersion ([version]'7.5.9') -DotNetMajor 10 + } | Should -Throw '*CLR mismatch*PowerShell 7.5*expected CLR 9*detected CLR 10*' + } + + It 'rejects malformed JSON policy data' { + $policyPath = Join-Path -Path $TestDrive -ChildPath 'malformed.json' + Set-Content -LiteralPath $policyPath -Value '{ invalid json' -Encoding utf8 + + { + Get-DPRuntimeProfile -PolicyPath $policyPath -PowerShellVersion ([version]'7.6.4') -DotNetMajor 10 + } | Should -Throw '*malformed*' + } + + It 'rejects duplicate PowerShell mappings' { + $policyPath = Join-Path -Path $TestDrive -ChildPath 'duplicate.json' + $policy = Get-Content -LiteralPath $script:RuntimePolicyPath -Raw | ConvertFrom-Json + $policy.profiles = @($policy.profiles) + @($policy.profiles[0]) + $policy | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $policyPath -Encoding utf8 + + { + Get-DPRuntimeProfile -PolicyPath $policyPath -PowerShellVersion ([version]'7.4.18') -DotNetMajor 8 + } | Should -Throw '*duplicate*PowerShell 7.4*' + } + + It 'rejects a TFM that does not correspond to the declared CLR major' { + $policyPath = Join-Path -Path $TestDrive -ChildPath 'invalid-tfm.json' + $policy = Get-Content -LiteralPath $script:RuntimePolicyPath -Raw | ConvertFrom-Json + $policy.profiles[2].targetFramework = 'net9.0' + $policy | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $policyPath -Encoding utf8 + + { + Get-DPRuntimeProfile -PolicyPath $policyPath -PowerShellVersion ([version]'7.6.4') -DotNetMajor 10 + } | Should -Throw '*targetFramework*net10.0*' + } + + It 'rejects a profile without complete host-provided assembly mappings' { + $policyPath = Join-Path -Path $TestDrive -ChildPath 'missing-host-map.json' + $policy = Get-Content -LiteralPath $script:RuntimePolicyPath -Raw | ConvertFrom-Json + $policy.profiles[0].hostProvidedAssemblyNames.PSObject.Properties.Remove('linux') + $policy | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $policyPath -Encoding utf8 + + { + Get-DPRuntimeProfile -PolicyPath $policyPath -PowerShellVersion ([version]'7.4.18') -DotNetMajor 8 + } | Should -Throw '*hostProvidedAssemblyNames*linux*' + } +} diff --git a/tests/Unit/RuntimeProvisioning.Tests.ps1 b/tests/Unit/RuntimeProvisioning.Tests.ps1 new file mode 100644 index 00000000..a37269fe --- /dev/null +++ b/tests/Unit/RuntimeProvisioning.Tests.ps1 @@ -0,0 +1,137 @@ +BeforeAll { + $script:RepositoryRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent + $script:ProvisionerPath = Join-Path -Path $script:RepositoryRoot -ChildPath 'tools/Install-DLLPickleTestPowerShell.ps1' + $script:MatrixPath = Join-Path -Path $script:RepositoryRoot -ChildPath 'build/powershell-test-matrix.json' + + function Write-RuntimeProvisioningMatrixFixture { + param( + [Parameter(Mandatory)] + [string]$Path + ) + + [ordered]@{ + schemaVersion = 1 + profiles = @( + [ordered]@{ + powerShellVersion = $PSVersionTable.PSVersion.ToString() + powerShellMajor = $PSVersionTable.PSVersion.Major + powerShellMinor = $PSVersionTable.PSVersion.Minor + dotnetMajor = [Environment]::Version.Major + dotnetRuntimeVersion = [Environment]::Version.ToString() + targetFramework = 'net{0}.0' -f [Environment]::Version.Major + } + ) + } | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath $Path -Encoding UTF8 + + return $Path + } +} + +Describe 'Exact PowerShell runtime provisioning' -Tag 'Unit' { + It 'validates an explicit stock executable without multi-pwsh' { + $currentExecutable = (Get-Process -Id $PID).Path + $currentVersion = $PSVersionTable.PSVersion.ToString() + $TestMatrixPath = Write-RuntimeProvisioningMatrixFixture -Path (Join-Path $TestDrive 'runtime-matrix.json') + + $result = & $script:ProvisionerPath -PowerShellExecutable $currentExecutable -PowerShellVersion $currentVersion -MatrixPath $TestMatrixPath -PassThru + + $result.Provider | Should -Be 'ExplicitExecutable' + $result.PowerShellVersion | Should -Be $currentVersion + $result.DotNetVersion | Should -Be ([Environment]::Version.ToString()) + $result.DotNetMajor | Should -Be ([Environment]::Version.Major) + $result.TargetFramework | Should -Be ('net{0}.0' -f [Environment]::Version.Major) + $result.ExecutablePath | Should -Be (Resolve-Path -LiteralPath $currentExecutable).Path + } + + It 'rejects an explicit executable that reports a different servicing patch' { + $currentExecutable = (Get-Process -Id $PID).Path + $currentVersion = $PSVersionTable.PSVersion.ToString() + $matrix = Get-Content -LiteralPath $script:MatrixPath -Raw | ConvertFrom-Json + $differentDeclaredVersion = @($matrix.profiles.powerShellVersion | Where-Object { $_ -ne $currentVersion })[0] + + $differentDeclaredVersion | Should -Not -BeNullOrEmpty + + { + & $script:ProvisionerPath -PowerShellExecutable $currentExecutable -PowerShellVersion $differentDeclaredVersion + } | Should -Throw '*version mismatch*' + } + + It 'rejects a matrix entry with a different bundled .NET runtime patch' { + $currentExecutable = (Get-Process -Id $PID).Path + $currentVersion = $PSVersionTable.PSVersion.ToString() + $TestMatrixPath = Write-RuntimeProvisioningMatrixFixture -Path (Join-Path $TestDrive 'runtime-version-mismatch.json') + $matrix = Get-Content -LiteralPath $TestMatrixPath -Raw | ConvertFrom-Json + $runtimeVersion = [Environment]::Version + $matrix.profiles[0].dotnetRuntimeVersion = '{0}.{1}.{2}' -f $runtimeVersion.Major, $runtimeVersion.Minor, ($runtimeVersion.Build + 1) + $matrix | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath $TestMatrixPath -Encoding UTF8 + + { + & $script:ProvisionerPath -PowerShellExecutable $currentExecutable -PowerShellVersion $currentVersion -MatrixPath $TestMatrixPath + } | Should -Throw '*CLR runtime version mismatch*' + } + + It 'discovers the runtime patch for an explicitly pending lifecycle candidate' { + $currentExecutable = (Get-Process -Id $PID).Path + $currentVersion = $PSVersionTable.PSVersion.ToString() + $TestMatrixPath = Write-RuntimeProvisioningMatrixFixture -Path (Join-Path $TestDrive 'pending-runtime-version.json') + $matrix = Get-Content -LiteralPath $TestMatrixPath -Raw | ConvertFrom-Json + $matrix.profiles[0].dotnetRuntimeVersion = $null + $matrix | Add-Member -NotePropertyName candidateValidationPending -NotePropertyValue $true + $matrix | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath $TestMatrixPath -Encoding UTF8 + + $result = & $script:ProvisionerPath -PowerShellExecutable $currentExecutable -PowerShellVersion $currentVersion -MatrixPath $TestMatrixPath -PassThru + + $result.DotNetVersion | Should -Be ([Environment]::Version.ToString()) + $result.DotNetMajor | Should -Be ([Environment]::Version.Major) + } + + It 'rejects a missing runtime patch outside lifecycle candidate validation' { + $currentExecutable = (Get-Process -Id $PID).Path + $currentVersion = $PSVersionTable.PSVersion.ToString() + $TestMatrixPath = Write-RuntimeProvisioningMatrixFixture -Path (Join-Path $TestDrive 'missing-runtime-version.json') + $matrix = Get-Content -LiteralPath $TestMatrixPath -Raw | ConvertFrom-Json + $matrix.profiles[0].dotnetRuntimeVersion = $null + $matrix | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath $TestMatrixPath -Encoding UTF8 + + { + & $script:ProvisionerPath -PowerShellExecutable $currentExecutable -PowerShellVersion $currentVersion -MatrixPath $TestMatrixPath + } | Should -Throw '*has no dotnetRuntimeVersion*' + } + + It 'still enforces the declared CLR major for a pending lifecycle candidate' { + $currentExecutable = (Get-Process -Id $PID).Path + $currentVersion = $PSVersionTable.PSVersion.ToString() + $TestMatrixPath = Write-RuntimeProvisioningMatrixFixture -Path (Join-Path $TestDrive 'pending-runtime-major-mismatch.json') + $matrix = Get-Content -LiteralPath $TestMatrixPath -Raw | ConvertFrom-Json + $matrix.profiles[0].dotnetRuntimeVersion = $null + $matrix.profiles[0].dotnetMajor = [Environment]::Version.Major + 1 + $matrix | Add-Member -NotePropertyName candidateValidationPending -NotePropertyValue $true + $matrix | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath $TestMatrixPath -Encoding UTF8 + + { + & $script:ProvisionerPath -PowerShellExecutable $currentExecutable -PowerShellVersion $currentVersion -MatrixPath $TestMatrixPath + } | Should -Throw '*CLR mismatch*' + } + + It 'contains no floating release selector or PATH mutation' { + $source = Get-Content -LiteralPath $script:ProvisionerPath -Raw + + $source | Should -Not -Match 'install\s+(stable|lts|7\.4(?!\.18)|7\.5(?!\.9)|7\.6(?!\.4))' + $source | Should -Not -Match '\$env:PATH\s*=' + $source | Should -Match ([regex]::Escape('--no-add-path')) + $source | Should -Not -Match 'multi-pwsh\s+host|pwsh-7\.' + } + + It 'derives official payload paths and validates provider checksums' { + $source = Get-Content -LiteralPath $script:ProvisionerPath -Raw + $matrix = Get-Content -LiteralPath $script:MatrixPath -Raw | ConvertFrom-Json + + $source | Should -Match ([regex]::Escape("Join-Path 'multi' $ExactVersion")) + $source | Should -Match ([regex]::Escape('Test-PathWithinRoot')) + $source | Should -Not -Match '(?ms)if \(-not \(Test-Path -LiteralPath \$ExpectedExecutable -PathType Leaf\)\) \{\s+Get-VerifiedDownload -Uri \$Archive\[0\]\.downloadUrl' + $source | Should -Match '(?ms)Revalidate the immutable archive.*Get-VerifiedDownload -Uri \$Archive\[0\]\.downloadUrl.*Expand-TestRuntimeArchive -ArchivePath \$CachePath' + @($matrix.archiveAssets) | Should -HaveCount 9 + @($matrix.provisioning.optionalProvider.assets) | Should -HaveCount 3 + @($matrix.archiveAssets.sha256 + $matrix.provisioning.optionalProvider.assets.sha256 | Where-Object { $_ -notmatch '^[a-f0-9]{64}$' }) | Should -BeNullOrEmpty + } +} diff --git a/tests/Unit/SupportDocumentation.Tests.ps1 b/tests/Unit/SupportDocumentation.Tests.ps1 new file mode 100644 index 00000000..1c73a9e1 --- /dev/null +++ b/tests/Unit/SupportDocumentation.Tests.ps1 @@ -0,0 +1,150 @@ +BeforeAll { + $script:ProjectRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path + $script:GeneratorPath = Join-Path $script:ProjectRoot 'tools\New-DLLPickleSupportDocumentation.ps1' + $script:ReadmePath = Join-Path $script:ProjectRoot 'README.md' + $script:ArchitecturePath = Join-Path $script:ProjectRoot 'docs\Architecture.md' + $script:DependencyDocPath = Join-Path $script:ProjectRoot 'docs\DEPENDENCIES.md' +} + +Describe 'Generated support documentation' -Tag 'Unit' { + It 'matches the canonical support and dependency policies' { + { & $script:GeneratorPath -Check } | Should -Not -Throw + } + + It 'renders repository-canonical CRLF endings on every host' { + $OutputDirectory = Join-Path $TestDrive 'generated' + + $null = & $script:GeneratorPath -OutputDirectory $OutputDirectory + + foreach ($DocumentName in @('Support-Matrix.md', 'Compatibility-Evidence.md')) { + $Document = [System.IO.File]::ReadAllText((Join-Path $OutputDirectory $DocumentName)) + $Document | Should -Match "`r`n" + ($Document -replace "`r`n", '') | Should -Not -Match "`n" + } + } + + It 'renders accepted committed profile evidence instead of expiring artifact placeholders' { + $FixtureRoot = Join-Path $TestDrive 'accepted-evidence-docs' + $EvidenceDirectory = Join-Path $FixtureRoot 'profile-evidence' + $null = New-Item -Path $EvidenceDirectory -ItemType Directory -Force + $SupportPolicyPath = Join-Path $FixtureRoot 'support.json' + $TestMatrixPath = Join-Path $FixtureRoot 'matrix.json' + $DependencyPolicyPath = Join-Path $FixtureRoot 'dependency.json' + $OutputDirectory = Join-Path $FixtureRoot 'generated' + $EvidencePath = Join-Path $EvidenceDirectory 'ps7.6-net10.0-windows-arm64.json' + + $EvidenceContent = [ordered]@{ + profile = [ordered]@{ profileKey = 'ps7.6-net10.0-windows-arm64' } + modules = @( + [ordered]@{ + name = 'Synthetic.One' + version = '1.10.0' + selectedAssets = @( + [ordered]@{ + assemblyName = 'Microsoft.Identity.Client' + assemblyVersion = '4.82.1.0' + assemblyLoadContext = 'Default' + selectedAsset = 'upstream:Synthetic.One/1.10.0/lib/Microsoft.Identity.Client.dll' + } + ) + } + ) + } + $CanonicalContent = $EvidenceContent | ConvertTo-Json -Depth 100 -Compress + $EvidenceBytes = [System.Text.Encoding]::UTF8.GetBytes($CanonicalContent) + $EvidenceFingerprint = [System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::HashData($EvidenceBytes)).Replace('-', '').ToLowerInvariant() + [ordered]@{ + schemaVersion = 1 + contentFingerprint = $EvidenceFingerprint + provenance = [ordered]@{ + sourceRunId = '12345' + sourceRunUrl = 'https://example.invalid/runs/12345' + capturedAtUtc = '2026-08-10T02:30:00Z' + } + content = $EvidenceContent + } | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $EvidencePath -Encoding UTF8 + + [ordered]@{ + profiles = @( + [ordered]@{ + powerShellMajor = 7 + powerShellMinor = 6 + dotnetMajor = 10 + targetFramework = 'net10.0' + } + ) + } | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $SupportPolicyPath -Encoding UTF8 + [ordered]@{ + lastVerifiedUtc = '2026-08-09T12:00:00Z' + profiles = @( + [ordered]@{ + powerShellMajor = 7 + powerShellMinor = 6 + powerShellVersion = '7.6.4' + dotnetMajor = 10 + dotnetRuntimeVersion = '10.0.10' + targetFramework = 'net10.0' + lifecycleState = 'STS' + lifecycleEndDate = '2026-11-10' + } + ) + lanes = @( + [ordered]@{ + platform = 'windows' + architecture = 'arm64' + } + ) + } | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $TestMatrixPath -Encoding UTF8 + [ordered]@{ + monitoredModules = @( + [ordered]@{ + name = 'Synthetic.One' + authenticatedReadOnlyProbeCommand = 'Connect-Synthetic; Get-SyntheticReadOnly' + } + ) + runtimeProfiles = @( + [ordered]@{ + powerShellLine = '7.6' + targetFramework = 'net10.0' + platforms = @('windows') + baselines = [ordered]@{ + windows = [ordered]@{ + status = 'accepted' + evidencePath = 'profile-evidence/ps7.6-net10.0-windows-arm64.json' + evidenceFingerprint = $EvidenceFingerprint + } + } + } + ) + } | ConvertTo-Json -Depth 15 | Set-Content -LiteralPath $DependencyPolicyPath -Encoding UTF8 + + $null = & $script:GeneratorPath -SupportPolicyPath $SupportPolicyPath -TestMatrixPath $TestMatrixPath -DependencyPolicyPath $DependencyPolicyPath -OutputDirectory $OutputDirectory + $Compatibility = Get-Content -LiteralPath (Join-Path $OutputDirectory 'Compatibility-Evidence.md') -Raw + $Compatibility | Should -Match 'Synthetic\.One \| 1\.10\.0' + $Compatibility | Should -Match 'upstream:Synthetic\.One/1\.10\.0/lib/Microsoft\.Identity\.Client\.dll' + $Compatibility | Should -Match 'Microsoft\.Identity\.Client.*4\.82\.1\.0.*Default' + $Compatibility | Should -Match '\[12345\]\(https://example\.invalid/runs/12345\)' + $Compatibility | Should -Match '\| 2026-08-10 \| \[12345\]' + } + + It 'keeps the primary documentation linked to the generated support contract' { + Get-Content -LiteralPath $script:ReadmePath -Raw | Should -Match 'generated/Support-Matrix\.md' + Get-Content -LiteralPath $script:ArchitecturePath -Raw | Should -Match 'generated/Support-Matrix\.md' + Get-Content -LiteralPath $script:DependencyDocPath -Raw | Should -Match 'generated/Compatibility-Evidence\.md' + } + + It 'separates Microsoft support, upstream evidence, and optional CI tooling claims' { + $SupportMatrix = Get-Content -LiteralPath (Join-Path $script:ProjectRoot 'docs\generated\Support-Matrix.md') -Raw + $Compatibility = Get-Content -LiteralPath (Join-Path $script:ProjectRoot 'docs\generated\Compatibility-Evidence.md') -Raw + $SupportMatrix | Should -Match 'Microsoft-supported runtime contract' + $SupportMatrix | Should -Match 'multi-pwsh.*optional' + $Compatibility | Should -Match 'release-gating gaps' + $Compatibility | Should -Match 'process-isolation requirement' + $Compatibility | Should -Match 'Issue #34' + $Compatibility | Should -Match 'PR #215' + $Compatibility | Should -Match 'Issue #242' + $Compatibility | Should -Match 'not executed without approved credentials' + $Compatibility | Should -Match 'manual transition record for version `3\.0\.0`' + $Compatibility | Should -Match 'not least-privilege workload-identity proof' + } +} diff --git a/tests/Unit/TfmAlignment.Tests.ps1 b/tests/Unit/TfmAlignment.Tests.ps1 index c2a497f7..57b93ab9 100644 --- a/tests/Unit/TfmAlignment.Tests.ps1 +++ b/tests/Unit/TfmAlignment.Tests.ps1 @@ -50,51 +50,82 @@ BeforeAll { $PackageDirectory } - # Builds a synthetic policy + lock file + NuGet-style packages root for the policy-driven mode. + # Builds a synthetic policy, lock file, and NuGet target graph for policy-driven mode. function Get-FixturePolicyContext { param( - [Parameter(Mandatory)] - [string[]]$AlignedLibFramework + [Parameter()] + [AllowEmptyCollection()] + [string[]]$RuntimeAssetFramework = @('net8.0'), + + [Parameter()] + [AllowEmptyCollection()] + [string[]]$CompileAssetFramework = @(), + + [Parameter()] + [string[]]$PolicyTargetFramework = @('net8.0') ) $Context = Join-Path $TestDrive ([System.Guid]::NewGuid().ToString('n')) - $PackagesRoot = Join-Path $Context 'packages' - $null = New-Item -Path $PackagesRoot -ItemType Directory -Force + $null = New-Item -Path $Context -ItemType Directory -Force $PolicyPath = Join-Path $Context 'policy.json' @{ preload = @( @{ packageName = 'Contoso.Fixture'; assemblyName = 'Contoso.Fixture'; classification = 'preload' } ) + runtimeProfiles = @( + foreach ($TargetFramework in $PolicyTargetFramework) { + @{ targetFramework = $TargetFramework } + } + ) } | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $PolicyPath -Encoding utf8 $LockPath = Join-Path $Context 'packages.lock.json' + $LockDependencies = [ordered]@{} + foreach ($TargetFramework in $PolicyTargetFramework) { + $LockDependencies[$TargetFramework] = @{ + 'Contoso.Fixture' = @{ type = 'Direct'; resolved = '1.2.3' } + } + } @{ version = 1 - dependencies = @{ - 'net8.0' = @{ - 'Contoso.Fixture' = @{ type = 'Direct'; resolved = '1.2.3' } - } - } + dependencies = $LockDependencies } | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $LockPath -Encoding utf8 - # NuGet lowercases both the package id folder and the version folder under the global cache. - $RestoredLib = Join-Path $PackagesRoot 'contoso.fixture\1.2.3\lib' - foreach ($Tfm in $AlignedLibFramework) { - $TfmDirectory = Join-Path $RestoredLib $Tfm - $null = New-Item -Path $TfmDirectory -ItemType Directory -Force - Set-Content -LiteralPath (Join-Path $TfmDirectory 'Contoso.Fixture.dll') -Value 'fixture' -Encoding utf8 + $AssetsPath = Join-Path $Context 'project.assets.json' + $Targets = [ordered]@{} + foreach ($TargetFramework in $PolicyTargetFramework) { + $RuntimeAssets = if ($TargetFramework -in $RuntimeAssetFramework) { + @{ "lib/$TargetFramework/Contoso.Fixture.dll" = @{} } + } else { + @{} + } + $CompileAssets = if ($TargetFramework -in $CompileAssetFramework) { + @{ "ref/$TargetFramework/Contoso.Fixture.dll" = @{} } + } else { + @{} + } + $Targets[$TargetFramework] = @{ + 'Contoso.Fixture/1.2.3' = @{ + type = 'package' + runtime = $RuntimeAssets + compile = $CompileAssets + } + } } + @{ version = 4; targets = $Targets } | + ConvertTo-Json -Depth 15 | + Set-Content -LiteralPath $AssetsPath -Encoding utf8 [PSCustomObject]@{ - PolicyPath = $PolicyPath - LockPath = $LockPath - PackagesRoot = $PackagesRoot + PolicyPath = $PolicyPath + LockPath = $LockPath + AssetsPath = $AssetsPath } } } -Describe 'Test-DLLPickleTfmAlignment net8.0 compatibility decisions' -Tag 'Unit' { +Describe 'Test-DLLPickleTfmAlignment package-directory compatibility decisions' -Tag 'Unit' { It 'treats as net8.0-aligned' -ForEach @( @{ Tfm = 'net8.0' } @{ Tfm = 'net6.0' } @@ -122,6 +153,18 @@ Describe 'Test-DLLPickleTfmAlignment net8.0 compatibility decisions' -Tag 'Unit' $Result = & $script:ToolPath -PackageDirectory $Directory $Result.IsAligned | Should -BeFalse } + + It 'evaluates package assets against the selected supported target framework' -ForEach @( + @{ RuntimeTfm = 'net9.0'; AssetTfm = 'net9.0'; Expected = $true } + @{ RuntimeTfm = 'net10.0'; AssetTfm = 'net10.0'; Expected = $true } + @{ RuntimeTfm = 'net9.0'; AssetTfm = 'net10.0'; Expected = $false } + ) { + $Directory = Get-FixturePackageDirectory -LibFramework @($AssetTfm) + $Result = & $script:ToolPath -PackageDirectory $Directory -TargetFramework $RuntimeTfm + + $Result.TargetFramework | Should -BeExactly $RuntimeTfm + $Result.IsAligned | Should -Be $Expected + } } Describe 'Test-DLLPickleTfmAlignment package inspection' -Tag 'Unit' { @@ -163,9 +206,9 @@ Describe 'Test-DLLPickleTfmAlignment package inspection' -Tag 'Unit' { Describe 'Test-DLLPickleTfmAlignment policy-driven inspection' -Tag 'Unit' { It 'reports an aggregate aligned result when every preload package is aligned' { - $Fixture = Get-FixturePolicyContext -AlignedLibFramework @('net8.0', 'netstandard2.0') + $Fixture = Get-FixturePolicyContext -RuntimeAssetFramework @('net8.0') $OutputPath = Join-Path $TestDrive 'aligned-report.json' - $Report = & $script:ToolPath -PolicyPath $Fixture.PolicyPath -LockFilePath $Fixture.LockPath -PackagesRoot $Fixture.PackagesRoot -OutputPath $OutputPath + $Report = & $script:ToolPath -PolicyPath $Fixture.PolicyPath -LockFilePath $Fixture.LockPath -ProjectAssetsPath $Fixture.AssetsPath -OutputPath $OutputPath $Report.IsAligned | Should -BeTrue @($Report.Packages).Count | Should -Be 1 @@ -176,16 +219,60 @@ Describe 'Test-DLLPickleTfmAlignment policy-driven inspection' -Tag 'Unit' { } It 'reports an aggregate misaligned result and names the offending package' { - $Fixture = Get-FixturePolicyContext -AlignedLibFramework @('net48') - $Report = & $script:ToolPath -PolicyPath $Fixture.PolicyPath -LockFilePath $Fixture.LockPath -PackagesRoot $Fixture.PackagesRoot + $Fixture = Get-FixturePolicyContext -RuntimeAssetFramework @() + $Report = & $script:ToolPath -PolicyPath $Fixture.PolicyPath -LockFilePath $Fixture.LockPath -ProjectAssetsPath $Fixture.AssetsPath $Report.IsAligned | Should -BeFalse - @($Report.Misaligned) | Should -Contain 'Contoso.Fixture' + @($Report.Misaligned) | Should -Contain 'net8.0/Contoso.Fixture' } It 'throws in strict mode when a preload package is misaligned' { - $Fixture = Get-FixturePolicyContext -AlignedLibFramework @('net48') - { & $script:ToolPath -PolicyPath $Fixture.PolicyPath -LockFilePath $Fixture.LockPath -PackagesRoot $Fixture.PackagesRoot -Strict } | + $Fixture = Get-FixturePolicyContext -RuntimeAssetFramework @() + { & $script:ToolPath -PolicyPath $Fixture.PolicyPath -LockFilePath $Fixture.LockPath -ProjectAssetsPath $Fixture.AssetsPath -Strict } | Should -Throw '*TFM*' } + + It 'uses NuGet project.assets.json as the policy-mode compatibility authority' { + $Fixture = Get-FixturePolicyContext -RuntimeAssetFramework @('net8.0') + $Assets = Get-Content -LiteralPath $Fixture.AssetsPath -Raw | ConvertFrom-Json + $Assets.targets.'net8.0' = [PSCustomObject]@{} + $Assets | ConvertTo-Json -Depth 15 | Set-Content -LiteralPath $Fixture.AssetsPath -Encoding utf8 + + $Report = & $script:ToolPath -PolicyPath $Fixture.PolicyPath -LockFilePath $Fixture.LockPath -ProjectAssetsPath $Fixture.AssetsPath + + $Report.IsAligned | Should -BeFalse + $Report.Packages[0].Reason | Should -Match "selected no 'Contoso\.Fixture/1\.2\.3' entry" + } + + It 'rejects compile-only selections because no runtime assembly can be loaded' { + $Fixture = Get-FixturePolicyContext -RuntimeAssetFramework @() -CompileAssetFramework @('net8.0') + + $Report = & $script:ToolPath -PolicyPath $Fixture.PolicyPath -LockFilePath $Fixture.LockPath -ProjectAssetsPath $Fixture.AssetsPath + + $Report.IsAligned | Should -BeFalse + $Report.Packages[0].SelectedAssets | Should -BeNullOrEmpty + $Report.Packages[0].Reason | Should -Match 'compile-only' + } + + It 'validates every framework declared by a multi-framework policy' { + $Frameworks = @('net8.0', 'net9.0', 'net10.0') + $Fixture = Get-FixturePolicyContext -PolicyTargetFramework $Frameworks -RuntimeAssetFramework @('net8.0') + + $Report = & $script:ToolPath -PolicyPath $Fixture.PolicyPath -LockFilePath $Fixture.LockPath -ProjectAssetsPath $Fixture.AssetsPath + + @($Report.Packages) | Should -HaveCount 3 + ($Report.Packages | Where-Object TargetFramework -EQ 'net8.0').IsAligned | Should -BeTrue + ($Report.Packages | Where-Object TargetFramework -EQ 'net9.0').IsAligned | Should -BeFalse + ($Report.Packages | Where-Object TargetFramework -EQ 'net10.0').IsAligned | Should -BeFalse + } + + It 'rejects a runtime profile without a target framework' { + $Fixture = Get-FixturePolicyContext + $Policy = Get-Content -LiteralPath $Fixture.PolicyPath -Raw | ConvertFrom-Json + $Policy.runtimeProfiles = @($Policy.runtimeProfiles) + @([PSCustomObject]@{}) + $Policy | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $Fixture.PolicyPath -Encoding utf8 + + { & $script:ToolPath -PolicyPath $Fixture.PolicyPath -LockFilePath $Fixture.LockPath -ProjectAssetsPath $Fixture.AssetsPath -Strict } | + Should -Throw '*runtimeProfiles*without a targetFramework*' + } } diff --git a/tests/Unit/UpstreamInventoryProfile.Tests.ps1 b/tests/Unit/UpstreamInventoryProfile.Tests.ps1 new file mode 100644 index 00000000..4b4ef768 --- /dev/null +++ b/tests/Unit/UpstreamInventoryProfile.Tests.ps1 @@ -0,0 +1,111 @@ +BeforeAll { + $script:RepositoryRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent + $script:InventoryToolPath = Join-Path -Path $script:RepositoryRoot -ChildPath 'tools/Get-DLLPickleUpstreamInventory.ps1' + + function Write-UpstreamInventoryRuntimeMatrixFixture { + param( + [Parameter(Mandatory)] + [string]$Path + ) + + [ordered]@{ + schemaVersion = 1 + profiles = @( + [ordered]@{ + powerShellVersion = $PSVersionTable.PSVersion.ToString() + powerShellMajor = $PSVersionTable.PSVersion.Major + powerShellMinor = $PSVersionTable.PSVersion.Minor + dotnetMajor = [Environment]::Version.Major + targetFramework = 'net{0}.0' -f [Environment]::Version.Major + } + ) + } | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath $Path -Encoding UTF8 + + return $Path + } + + function Get-UpstreamInventoryFixture { + $root = Join-Path -Path $TestDrive -ChildPath ([guid]::NewGuid().ToString('n')) + $moduleCache = Join-Path -Path $root -ChildPath 'modules' + $moduleVersionRoot = Join-Path -Path $moduleCache -ChildPath 'Contoso.ProfileProbe/1.0.0' + $null = New-Item -Path $moduleVersionRoot -ItemType Directory -Force + Set-Content -LiteralPath (Join-Path $moduleVersionRoot 'Contoso.ProfileProbe.psm1') -Value '# profile probe fixture' -Encoding utf8 + $decoyDirectory = Join-Path -Path $moduleVersionRoot -ChildPath 'lib/decoy' + $null = New-Item -Path $decoyDirectory -ItemType Directory -Force + $decoyAssemblyPath = Join-Path -Path $decoyDirectory -ChildPath 'System.Management.Automation.dll' + Copy-Item -LiteralPath ([System.Management.Automation.PSObject].Assembly.Location) -Destination $decoyAssemblyPath + @' +@{ + RootModule = 'Contoso.ProfileProbe.psm1' + ModuleVersion = '1.0.0' + GUID = '471d52e5-d025-47f6-9706-f0f3b1bdb198' + PowerShellVersion = '7.4' + CompatiblePSEditions = @('Core') +} +'@ | Set-Content -LiteralPath (Join-Path $moduleVersionRoot 'Contoso.ProfileProbe.psd1') -Encoding utf8 + + $policyPath = Join-Path -Path $root -ChildPath 'policy.json' + [ordered]@{ + monitoredModules = @( + [ordered]@{ + name = 'Contoso.ProfileProbe' + umbrellaModule = 'Contoso.Umbrella' + repository = 'PSGallery' + purpose = 'Fixture' + deterministicProbeCommand = 'Get-Command Get-Item | Out-Null' + } + ) + trackedAssemblies = @('System.Management.Automation') + } | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $policyPath -Encoding utf8 + + [pscustomobject]@{ + PolicyPath = $policyPath + ModuleCachePath = $moduleCache + OutputPath = Join-Path -Path $root -ChildPath 'inventory.json' + DecoyAssemblyPath = $decoyAssemblyPath + } + } +} + +Describe 'Profile-aware upstream inventory' -Tag 'Unit' { + It 'records exact runtime identity and only actually selected tracked assets' { + $fixture = Get-UpstreamInventoryFixture + $TestMatrixPath = Write-UpstreamInventoryRuntimeMatrixFixture -Path (Join-Path $TestDrive 'runtime-matrix.json') + $parameters = @{ + PolicyPath = $fixture.PolicyPath + TestMatrixPath = $TestMatrixPath + ModuleCachePath = $fixture.ModuleCachePath + OutputPath = $fixture.OutputPath + PowerShellExecutable = [Environment]::ProcessPath + SkipDownload = $true + } + + $report = & $script:InventoryToolPath @parameters + + $report.SchemaVersion | Should -Be 2 + $report.ValidationTier | Should -Be 'DeterministicImportNoAuth' + $report.Profile.PowerShellVersion | Should -Be $PSVersionTable.PSVersion.ToString() + $report.Profile.DotNetMajor | Should -Be ([Environment]::Version.Major) + $report.Profile.TargetFramework | Should -Be ('net{0}.0' -f [Environment]::Version.Major) + $report.Profile.ExecutablePath | Should -Not -BeNullOrEmpty + $report.Profile.PSHome | Should -Not -BeNullOrEmpty + $report.Profile.Platform | Should -Not -BeNullOrEmpty + $report.Profile.Architecture | Should -Not -BeNullOrEmpty + + $module = $report.Modules[0] + $module.UmbrellaModule | Should -Be 'Contoso.Umbrella' + $module.ConstituentModule | Should -Be 'Contoso.ProfileProbe' + $module.ManifestPowerShellVersion | Should -Be '7.4' + @($module.CompatiblePSEditions) | Should -Contain 'Core' + $row = $module.TrackedAssemblies | Where-Object Name -eq 'System.Management.Automation' + $row.SelectedAssetPath | Should -Not -BeNullOrEmpty + $row.Sha256 | Should -Match '^[a-f0-9]{64}$' + $row.Alc | Should -Not -BeNullOrEmpty + $row.TargetFramework | Should -Be $report.Profile.TargetFramework + $row.Platform | Should -Be $report.Profile.Platform + $row.Architecture | Should -Be $report.Profile.Architecture + @($module.TrackedAssemblies) | Should -HaveCount 1 + $row.SelectedAssetPath | Should -Not -BeExactly $fixture.DecoyAssemblyPath + @($module.TrackedAssemblies.SelectedAssetPath) | Should -Not -Contain $fixture.DecoyAssemblyPath + } +} diff --git a/tests/Unit/UpstreamScenarioEvidence.Tests.ps1 b/tests/Unit/UpstreamScenarioEvidence.Tests.ps1 new file mode 100644 index 00000000..2f425453 --- /dev/null +++ b/tests/Unit/UpstreamScenarioEvidence.Tests.ps1 @@ -0,0 +1,128 @@ +BeforeAll { + $script:RepositoryRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent + $script:ToolPath = Join-Path $script:RepositoryRoot 'tools\New-DLLPickleUpstreamScenarioEvidence.ps1' +} + +Describe 'Deterministic upstream import-order evidence' -Tag 'Unit' { + It 'runs every configured order with and without DLLPickle under exact manifests' { + $ModuleCache = Join-Path $TestDrive 'modules' + $ModuleRows = @( + foreach ($Name in @('Synthetic.One', 'Synthetic.Two', 'Synthetic.Failure')) { + $ModuleRoot = Join-Path $ModuleCache "$Name\1.0.0" + $null = New-Item -Path $ModuleRoot -ItemType Directory -Force + Set-Content -LiteralPath (Join-Path $ModuleRoot "$Name.psm1") -Value "function Get-$($Name.Replace('.', '')) { 'ok' }" -Encoding UTF8 + $ManifestPath = Join-Path $ModuleRoot "$Name.psd1" + New-ModuleManifest -Path $ManifestPath -RootModule "$Name.psm1" -ModuleVersion '1.0.0' -FunctionsToExport @("Get-$($Name.Replace('.', ''))") + [PSCustomObject]@{ Name = $Name; ModuleManifestPath = $ManifestPath } + } + ) + $DllPickleRoot = Join-Path $TestDrive 'dllpickle' + $null = New-Item -Path $DllPickleRoot -ItemType Directory + Set-Content -LiteralPath (Join-Path $DllPickleRoot 'DLLPickle.psm1') -Value 'function Import-DPLibrary { param([switch]$SuppressLogo) }' -Encoding UTF8 + $DllPickleManifest = Join-Path $DllPickleRoot 'DLLPickle.psd1' + New-ModuleManifest -Path $DllPickleManifest -RootModule 'DLLPickle.psm1' -ModuleVersion '1.0.0' -FunctionsToExport @('Import-DPLibrary') + + $PowerShellLine = '{0}.{1}' -f $PSVersionTable.PSVersion.Major, $PSVersionTable.PSVersion.Minor + $TargetFramework = 'net{0}.0' -f [Environment]::Version.Major + $Platform = if ([Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([Runtime.InteropServices.OSPlatform]::Windows)) { + 'windows' + } elseif ([Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([Runtime.InteropServices.OSPlatform]::OSX)) { + 'macos' + } else { + 'linux' + } + $Architecture = [Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant() + $ProfileKey = "ps$PowerShellLine-$TargetFramework-$Platform-$Architecture" + $PolicyPath = Join-Path $TestDrive 'policy.json' + @{ + trackedAssemblies = @('System.Management.Automation') + monitoredModules = @( + @{ name = 'Synthetic.One'; deterministicProbeCommand = 'Get-Command Get-SyntheticOne | Out-Null' } + @{ name = 'Synthetic.Two'; deterministicProbeCommand = 'Get-Command Get-SyntheticTwo | Out-Null' } + @{ name = 'Synthetic.Failure'; deterministicProbeCommand = 'throw "expected synthetic failure"' } + ) + runtimeProfiles = @( + @{ + powerShellLine = $PowerShellLine + targetFramework = $TargetFramework + importOrders = @( + , @('Synthetic.One', 'Synthetic.Two') + , @('Synthetic.Two', 'Synthetic.One') + ) + knownConflictIds = @('synthetic-expected-failure') + } + ) + } | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $PolicyPath -Encoding UTF8 + $KnownConflictsPath = Join-Path $TestDrive 'known-conflicts.json' + @( + @{ + id = 'synthetic-expected-failure' + importOrders = @( + , @('Synthetic.Failure') + , @('Synthetic.One') + ) + requiresProcessIsolation = $true + } + ) | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $KnownConflictsPath -Encoding UTF8 + $InventoryPath = Join-Path $TestDrive 'inventory.json' + @{ + ProfileKey = $ProfileKey + ModuleCachePath = $ModuleCache + Profile = @{ + PowerShellVersion = $PSVersionTable.PSVersion.ToString() + PowerShellLine = $PowerShellLine + TargetFramework = $TargetFramework + PSHome = $PSHOME + Platform = $Platform + Architecture = $Architecture + } + Modules = @($ModuleRows) + } | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $InventoryPath -Encoding UTF8 + + $Parameters = @{ + PolicyPath = $PolicyPath + InventoryPath = $InventoryPath + PowerShellExecutable = [Environment]::ProcessPath + DLLPickleManifestPath = $DllPickleManifest + KnownConflictsPath = $KnownConflictsPath + OutputPath = Join-Path $TestDrive 'scenario-evidence.json' + Strict = $true + } + $First = & $script:ToolPath @Parameters + $Parameters.OutputPath = Join-Path $TestDrive 'scenario-evidence-second.json' + $Second = & $script:ToolPath @Parameters + + @($First.Scenarios) | Should -HaveCount 8 + @($First.Scenarios | Where-Object DllPicklePreloaded) | Should -HaveCount 4 + @($First.Scenarios | Where-Object { -not $_.DllPicklePreloaded }) | Should -HaveCount 4 + $ExpectedOrders = @( + 'Synthetic.Failure' + 'Synthetic.One' + 'Synthetic.One,Synthetic.Two' + 'Synthetic.Two,Synthetic.One' + ) + $ActualOrders = @($First.Scenarios | ForEach-Object { @($_.ImportOrder) -join ',' } | Sort-Object -Unique) + $ActualOrders | Should -Be $ExpectedOrders + foreach ($ExpectedOrder in $ExpectedOrders) { + $OrderScenarios = @($First.Scenarios | Where-Object { (@($_.ImportOrder) -join ',') -eq $ExpectedOrder }) + $OrderScenarios | Should -HaveCount 2 + @($OrderScenarios.DllPicklePreloaded | Sort-Object -Unique) | Should -Be @($false, $true) + @($OrderScenarios.OutcomeMatchesExpectation | Select-Object -Unique) | Should -Be @($true) + } + $First.Passed | Should -BeTrue + $First.WritesPerformed | Should -BeFalse + $KnownLimitationScenarios = @($First.Scenarios | Where-Object ScenarioId -EQ 'synthetic-expected-failure') + @($KnownLimitationScenarios | Where-Object Success) | Should -HaveCount 2 + @($KnownLimitationScenarios | Where-Object { -not $_.Success }) | Should -HaveCount 2 + foreach ($KnownLimitationScenario in $KnownLimitationScenarios) { + $KnownLimitationScenario.ExpectedLimitation | Should -BeTrue + $KnownLimitationScenario.ExpectedSuccess | Should -BeNullOrEmpty + $KnownLimitationScenario.OutcomePolicy | Should -Be 'observe-known-limitation' + $KnownLimitationScenario.OutcomeMatchesExpectation | Should -BeTrue + } + $First.ScenarioFingerprint | Should -BeExactly $Second.ScenarioFingerprint + $ObservedAssemblies = @($First.Scenarios | ForEach-Object { @($_.Assemblies) }) + @($ObservedAssemblies.Platform | Sort-Object -Unique) | Should -Be @($Platform) + @($ObservedAssemblies.Architecture | Sort-Object -Unique) | Should -Be @($Architecture) + } +} diff --git a/tests/Unit/WorkflowGuardrails.Tests.ps1 b/tests/Unit/WorkflowGuardrails.Tests.ps1 index 16db7e87..ab9f40a8 100644 --- a/tests/Unit/WorkflowGuardrails.Tests.ps1 +++ b/tests/Unit/WorkflowGuardrails.Tests.ps1 @@ -2,7 +2,11 @@ BeforeAll { $ProjectRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path $UpstreamWorkflow = Get-Content -LiteralPath (Join-Path $ProjectRoot '.github\workflows\Upstream-Compatibility.yml') -Raw $DependabotWorkflow = Get-Content -LiteralPath (Join-Path $ProjectRoot '.github\workflows\Dependabot-Auto-Approve.yml') -Raw + $DependabotConfig = Get-Content -LiteralPath (Join-Path $ProjectRoot '.github\dependabot.yml') -Raw $ReleaseWorkflow = Get-Content -LiteralPath (Join-Path $ProjectRoot '.github\workflows\Release-and-Publish.yml') -Raw + $ManualAuthValidator = Get-Content -LiteralPath (Join-Path $ProjectRoot 'tools\Test-DLLPickleManualAuthenticatedEvidence.ps1') -Raw + $LifecycleWorkflowPath = Join-Path $ProjectRoot '.github\workflows\PowerShell-Support-Lifecycle.yml' + $BuildWorkflow = Get-Content -LiteralPath (Join-Path $ProjectRoot '.github\workflows\Build Module.yml') -Raw } Describe 'Upstream compatibility workflow guardrails' -Tag 'Unit' { @@ -11,14 +15,25 @@ Describe 'Upstream compatibility workflow guardrails' -Tag 'Unit' { } It 'exposes an always-reported aggregate required check' { - $UpstreamWorkflow | Should -Match '(?ms)^ pr-gate:\s+name: Validate upstream compatibility tooling\s+needs: \[pr-changes, pr-smoke-validation\]\s+if: \$\{\{ always\(\) \}\}' + $UpstreamWorkflow | Should -Match '(?ms)^ pr-gate:\s+name: Validate upstream compatibility tooling\s+needs: \[pr-changes, pr-smoke-validation, profile-evidence-gate\]\s+if: \$\{\{ always\(\) \}\}' } It 'routes policy and fingerprint-generator changes through live validation' { $UpstreamWorkflow | Should -Match 'live_validation' $UpstreamWorkflow | Should -Match ([regex]::Escape('build/dependency-policy.json')) + $UpstreamWorkflow | Should -Match ([regex]::Escape("'^build/DLLPickle\.Build\.ps1$'")) + $UpstreamWorkflow | Should -Match ([regex]::Escape("'^build/DLLPickle\.Settings\.ps1$'")) + $UpstreamWorkflow | Should -Match ([regex]::Escape("'^build/DLLPickle\.Tooling\.ps1$'")) + $UpstreamWorkflow | Should -Match ([regex]::Escape("'^build/build-tool-versions\.json$'")) + $UpstreamWorkflow | Should -Match ([regex]::Escape("'^global\.json$'")) + ([regex]::Matches($UpstreamWorkflow, [regex]::Escape("'^global\.json$'"))).Count | Should -Be 2 $UpstreamWorkflow | Should -Match ([regex]::Escape('tools/Get-DLLPickleUpstreamInventory.ps1')) + $UpstreamWorkflow | Should -Match ([regex]::Escape("'^tools/Get-DLLPickleLoadedTrackedAssembly\.ps1$'")) + $UpstreamWorkflow | Should -Match ([regex]::Escape("'^tools/DLLPickle\.ProfileEvidence\.ps1$'")) + $UpstreamWorkflow | Should -Match ([regex]::Escape("'^tools/Invoke-DLLPickleBuild\.ps1$'")) $UpstreamWorkflow | Should -Match ([regex]::Escape('tools/New-DLLPickleConflictMatrix.ps1')) + $UpstreamWorkflow | Should -Match ([regex]::Escape('tools/New-DLLPickleUpstreamScenarioEvidence.ps1')) + $UpstreamWorkflow | Should -Match ([regex]::Escape("'^src/DLLPickle/'")) } It 'uploads compact JSON evidence and writes a job summary' { @@ -33,6 +48,22 @@ Describe 'Upstream compatibility workflow guardrails' -Tag 'Unit' { $UpstreamWorkflow | Should -Match ([regex]::Escape('tools/Test-DLLPickleTfmAlignment.ps1')) $UpstreamWorkflow | Should -Match ([regex]::Escape('tfm-alignment.json')) } + + It 'publishes profile-aware findings once per stable fingerprint' { + $UpstreamWorkflow | Should -Match ([regex]::Escape('tools/New-DLLPickleProfileEvidenceSummary.ps1')) + $UpstreamWorkflow | Should -Match ([regex]::Escape('tools/Test-DLLPickleFindingFingerprintReported.ps1')) + $UpstreamWorkflow | Should -Match ([regex]::Escape('$Summary.FindingMarker')) + $UpstreamWorkflow | Should -Match 'issues: write' + $UpstreamWorkflow | Should -Match ([regex]::Escape('suppressing a duplicate comment')) + } + + It 'uses exact-profile baselines and fingerprint-derived candidate branches for scheduled writes' { + $UpstreamWorkflow | Should -Match ([regex]::Escape('$ProfilePolicy[0].baselines.windows')) + $UpstreamWorkflow | Should -Not -Match ([regex]::Escape('$policy.baseline.conflictSurfaceFingerprint')) + $UpstreamWorkflow | Should -Match ([regex]::Escape('automation/upstream-compatibility-$($Fingerprint.Substring(0, 16))')) + $UpstreamWorkflow | Should -Match ([regex]::Escape('publication_fingerprint=')) + $UpstreamWorkflow | Should -Not -Match ([regex]::Escape('automation/upstream-compatibility-${{ github.run_id }}')) + } } Describe 'Dependabot auto-merge guardrails' -Tag 'Unit' { @@ -46,6 +77,29 @@ Describe 'Dependabot auto-merge guardrails' -Tag 'Unit' { $DependabotWorkflow | Should -Match ([regex]::Escape('Validate upstream compatibility tooling')) $DependabotWorkflow | Should -Match ([regex]::Escape('dependency-review')) } + + It 'routes a newly introduced conditional TFM pin to maintainer review' { + $DependabotWorkflow | Should -Match ([regex]::Escape('CONDITIONAL_TFM_ADDITION')) + $DependabotWorkflow | Should -Match ([regex]::Escape('\$\(TargetFramework\)')) + } + + It 'compares trusted base and candidate projects with a strict version-only validator' { + $DependabotWorkflow | Should -Match ([regex]::Escape('github.event.pull_request.base.sha')) + $DependabotWorkflow | Should -Match ([regex]::Escape('github.event.pull_request.head.sha')) + $DependabotWorkflow | Should -Match ([regex]::Escape('tools/Test-DLLPicklePackageReferenceUpdate.ps1')) + $DependabotWorkflow | Should -Match ([regex]::Escape('PROJECT_PATCH_VALID')) + } + + It 'refuses auto-approval when the Files API omits the project patch' { + $DependabotWorkflow | Should -Match ([regex]::Escape('__DLLPICKLE_PATCH_UNAVAILABLE__')) + $DependabotWorkflow | Should -Match ([regex]::Escape('PATCH_UNAVAILABLE')) + } + + It 'keeps runtime NuGet updates separate from CI toolchain updates' { + $DependabotConfig | Should -Match 'runtime-bundle-minor-patch' + $DependabotConfig | Should -Match 'ci-tooling-actions' + $DependabotConfig | Should -Match 'multi-pwsh.*CI provisioning policy' + } } Describe 'Dependabot major-version draft-PR flow' -Tag 'Unit' { @@ -57,11 +111,14 @@ Describe 'Dependabot major-version draft-PR flow' -Tag 'Unit' { $DependabotWorkflow | Should -Match ([regex]::Escape("update-type == 'version-update:semver-major'")) } - It 'posts structured notes covering the version delta, TFM alignment, conflict surface, and a maintainer checklist' { + It 'posts a per-TFM evidence index covering graph, assets, assembly and conflict deltas, size, scenarios, and maintainer review' { $DependabotWorkflow | Should -Match 'Version change' - $DependabotWorkflow | Should -Match 'TFM alignment' - $DependabotWorkflow | Should -Match ([regex]::Escape('Test-DLLPickleTfmAlignment.ps1')) - $DependabotWorkflow | Should -Match ([regex]::Escape('dependency-policy.json')) + $DependabotWorkflow | Should -Match 'Resolved graph \+ selected assets' + $DependabotWorkflow | Should -Match 'Added/removed/changed assemblies' + $DependabotWorkflow | Should -Match 'Conflict-surface delta' + $DependabotWorkflow | Should -Match 'Scenario outcomes' + $DependabotWorkflow | Should -Match ([regex]::Escape('artifact-size-baseline.json')) + $DependabotWorkflow | Should -Match ([regex]::Escape('Compatibility-Evidence.md')) $DependabotWorkflow | Should -Match 'Maintainer checklist' } @@ -73,6 +130,65 @@ Describe 'Dependabot major-version draft-PR flow' -Tag 'Unit' { } Describe 'Release publish gating guardrails' -Tag 'Unit' { + It 'requires protected exact-commit or narrowly bounded manual evidence before publication' { + $ReleaseWorkflow | Should -Match '(?ms)^ authenticated-release-gate:\s+name: Require Authenticated Compatibility' + $ReleaseWorkflow | Should -Match '(?ms)^ authenticated-release-gate:.*?permissions:\s+actions: read\s+contents: read' + $ReleaseWorkflow | Should -Match '(?m)^ needs: authenticated-release-gate\r?$' + $ReleaseWorkflow | Should -Match ([regex]::Escape('Authenticated-Compatibility.yml')) + $ReleaseWorkflow | Should -Match ([regex]::Escape('authenticated-compatibility-evidence')) + $ReleaseWorkflow | Should -Match ([regex]::Escape("'--commit', `$EvidenceSha")) + $ReleaseWorkflow | Should -Match ([regex]::Escape("Test-Path -LiteralPath `$ProtectedWorkflowPath -PathType Leaf")) + $ReleaseWorkflow | Should -Match ([regex]::Escape("if (`$LASTEXITCODE -ne 0)")) + $ReleaseWorkflow | Should -Match ([regex]::Escape("none has exactly one unexpired")) + $ReleaseWorkflow | Should -Match ([regex]::Escape("/repos/`$(`$env:GITHUB_REPOSITORY)/actions/runs/")) + $ReleaseWorkflow | Should -Not -Match ([regex]::Escape("'/repos/`$\{\{ github.repository \}\}")) + $ReleaseWorkflow | Should -Match ([regex]::Escape('steps.release-candidate.outputs.release_sha')) + $ReleaseWorkflow | Should -Match ([regex]::Escape('needs.authenticated-release-gate.outputs.release_sha')) + $ReleaseWorkflow | Should -Not -Match ([regex]::Escape('github.event.pull_request.head.sha')) + $ReleaseWorkflow | Should -Match ([regex]::Escape('requiredBeforeRelease')) + $ReleaseWorkflow | Should -Match ([regex]::Escape('writesAllowed -ne $false')) + $ReleaseWorkflow | Should -Match ([regex]::Escape('tools/Test-DLLPickleManualAuthenticatedEvidence.ps1')) + $ReleaseWorkflow | Should -Match ([regex]::Escape('initial-multitarget-major.json')) + $ReleaseWorkflow | Should -Match ([regex]::Escape('elseif (-not $ProtectedWorkflowConfigured -and (Test-Path -LiteralPath $env:MANUAL_AUTHENTICATED_EVIDENCE_PATH -PathType Leaf))')) + $ReleaseWorkflow | Should -Match ([regex]::Escape('protected authenticated workflow')) + $ReleaseWorkflow | Should -Match ([regex]::Escape('is configured but has no successful exact-commit evidence')) + $ReleaseWorkflow | Should -Match ([regex]::Escape('$EvidenceMode = ''manual-interactive-transition''')) + $ReleaseWorkflow | Should -Match ([regex]::Escape('[string]$NewVersion -ne $env:AUTHENTICATED_ALLOWED_RELEASE_VERSION')) + $ManualAuthValidator | Should -Match ([regex]::Escape("allowedReleaseVersion -ne '3.0.0'")) + $ReleaseWorkflow | Should -Not -Match 'skipAuthentication|bypassAuthentication|allowUnauthenticated' + } + + It 'fails closed on the runtime lifecycle policy before version analysis' { + $ReleaseWorkflow | Should -Match ([regex]::Escape('tools/Test-DLLPickleRuntimeProfilePolicy.ps1')) + $ReleaseWorkflow | Should -Match ([regex]::Escape('-Mode Release')) + $ReleaseWorkflow | Should -Match ([regex]::Escape('tools/Get-DLLPicklePowerShellSupportUpdate.ps1')) + $ReleaseWorkflow | Should -Match ([regex]::Escape('-RequireCurrent')) + $ReleaseWorkflow | Should -Match ([regex]::Escape('-LifecycleEvidencePath ./artifacts/lifecycle/powershell-support-update.json')) + $ReleaseWorkflow | Should -Match '(?ms)Refresh official PowerShell servicing state.*Validate supported PowerShell lifecycle policy' + } + + It 'runs profile-aware evidence and fail-closed baselines across the exact runtime matrix' { + $UpstreamWorkflow | Should -Match ([regex]::Escape('tools/New-DLLPicklePowerShellTestMatrix.ps1')) + $UpstreamWorkflow | Should -Match ([regex]::Escape('tools/Install-DLLPickleTestPowerShell.ps1')) + $UpstreamWorkflow | Should -Match ([regex]::Escape('tools/Test-DLLPickleProfileConflictBaseline.ps1')) + $UpstreamWorkflow | Should -Match ([regex]::Escape('tools/New-DLLPickleNormalizedProfileEvidence.ps1')) + $UpstreamWorkflow | Should -Match ([regex]::Escape('tools/New-DLLPickleUpstreamScenarioEvidence.ps1')) + $UpstreamWorkflow | Should -Match ([regex]::Escape('ScenarioEvidencePath')) + $UpstreamWorkflow | Should -Match ([regex]::Escape('NormalizedEvidencePath')) + $UpstreamWorkflow | Should -Match 'executed-two-orders-with-and-without-dllpickle' + $UpstreamWorkflow | Should -Match ([regex]::Escape('-PowerShellExecutable')) + $UpstreamWorkflow | Should -Match ([regex]::Escape('fromJson(needs.profile-matrix.outputs.matrix)')) + $UpstreamWorkflow | Should -Match ([regex]::Escape('not-run-no-approved-credentials')) + $UpstreamWorkflow | Should -Match ([regex]::Escape('writesPerformed = $false')) + } + + It 'revalidates composition and size after stamping the release artifact' { + $ReleaseWorkflow | Should -Match ([regex]::Escape('Test-DLLPicklePackageArtifact.ps1')) + $ReleaseWorkflow | Should -Match ([regex]::Escape('New-DLLPickleArtifactSizeReport.ps1')) + $ReleaseWorkflow | Should -Match ([regex]::Escape('SkipBuildOutputComparison')) + $ReleaseWorkflow | Should -Match ([regex]::Escape('stamped-release-policy-reports')) + } + It 'auto-triggers only on closed pull requests to main' { $ReleaseWorkflow | Should -Match '(?ms)on:\s+pull_request:\s+types:\s*\[closed\]' $ReleaseWorkflow | Should -Match '(?ms)branches:\s+- main' @@ -124,3 +240,94 @@ Describe 'Release publish gating guardrails' -Tag 'Unit' { $ReleaseWorkflow | Should -Match '(?ms)options:\s+- auto\s+- major\s+- minor\s+- patch' } } + +Describe 'PowerShell support lifecycle workflow guardrails' -Tag 'Unit' { + It 'runs a scheduled and manually dispatchable lifecycle check' { + $LifecycleWorkflowPath | Should -Exist + $lifecycleWorkflow = Get-Content -LiteralPath $LifecycleWorkflowPath -Raw + + $lifecycleWorkflow | Should -Match '(?m)^\s+schedule:' + $lifecycleWorkflow | Should -Match '(?m)^\s+workflow_dispatch:' + $lifecycleWorkflow | Should -Match ([regex]::Escape('tools/Test-DLLPickleRuntimeProfilePolicy.ps1')) + $lifecycleWorkflow | Should -Match ([regex]::Escape('-Mode Scheduled')) + $lifecycleWorkflow | Should -Match ([regex]::Escape('tools/Get-DLLPicklePowerShellSupportUpdate.ps1')) + $lifecycleWorkflow | Should -Match ([regex]::Escape('powershell-support-update.json')) + $lifecycleWorkflow | Should -Match ([regex]::Escape('tools/Update-DLLPicklePowerShellTestMatrix.ps1')) + $lifecycleWorkflow | Should -Match ([regex]::Escape('tools/Install-DLLPickleTestPowerShell.ps1')) + $lifecycleWorkflow | Should -Match ([regex]::Escape('fromJson(needs.discover.outputs.runtime_matrix)')) + $lifecycleWorkflow | Should -Match ([regex]::Escape('PatchProposalFingerprint')) + $lifecycleWorkflow | Should -Match ([regex]::Escape('SupportContractFingerprint')) + } + + It 'keeps discovery read-only and scopes repository writes to fingerprinted publication jobs' { + $lifecycleWorkflow = Get-Content -LiteralPath $LifecycleWorkflowPath -Raw + + $lifecycleWorkflow | Should -Match '(?ms)^permissions:\s+contents: read\s*$' + $lifecycleWorkflow | Should -Match '(?ms)^ finalize-patch-proposal:.*?permissions:\s+contents: write\s+pull-requests: write' + $lifecycleWorkflow | Should -Match '(?ms)^ support-contract-warning:.*?permissions:\s+contents: read\s+issues: write' + $lifecycleWorkflow | Should -Match ([regex]::Escape('automation/powershell-patch-$($Fingerprint.Substring(0, 16))')) + $lifecycleWorkflow | Should -Match ([regex]::Escape('tools/Test-DLLPickleFindingFingerprintReported.ps1')) + $lifecycleWorkflow | Should -Match 'gh\s+pr\s+create|@\(''pr'', ''create''' + $lifecycleWorkflow | Should -Match 'gh\s+issue\s+(create|comment)' + $lifecycleWorkflow | Should -Not -Match 'gh\s+pr\s+merge|git\s+push\s+--force' + } + + It 'treats runtime policy and SDK changes as build relevant' { + $BuildWorkflow | Should -Match ([regex]::Escape("'^global\.json$'")) + $BuildWorkflow | Should -Match ([regex]::Escape("'^build/powershell-test-matrix\.json$'")) + } +} + +Describe 'Exact PowerShell runtime matrix workflow guardrails' -Tag 'Unit' { + It 'generates the authoritative matrix from policy rather than a handwritten version list' { + $BuildWorkflow | Should -Match ([regex]::Escape('tools/New-DLLPicklePowerShellTestMatrix.ps1')) + $BuildWorkflow | Should -Match ([regex]::Escape('fromJson(needs.runtime-matrix.outputs.matrix)')) + } + + It 'provisions and directly invokes each exact stock executable in fresh processes' { + $BuildWorkflow | Should -Match ([regex]::Escape('tools/Install-DLLPickleTestPowerShell.ps1')) + $BuildWorkflow | Should -Match ([regex]::Escape('$env:DLLPICKLE_TEST_PWSH -NoLogo -NoProfile -NonInteractive')) + $BuildWorkflow | Should -Not -Match 'multi-pwsh\s+host|pwsh-7\.' + } + + It 'captures structured selected-bundle and assembly load-context evidence' { + $BuildWorkflow | Should -Match ([regex]::Escape('tools/New-DLLPickleRuntimeProfileEvidence.ps1')) + $BuildWorkflow | Should -Match ([regex]::Escape('runtime-evidence-')) + $BuildWorkflow | Should -Match 'upload-artifact@' + } + + It 'runs strict package composition and resolved-TFM checks in every exact-runtime cell' { + $RuntimeJob = [regex]::Match($BuildWorkflow, '(?ms)^ runtime-tests:.*?(?=^ dependency-change-report:)').Value + $RuntimeJob | Should -Match ([regex]::Escape('tools/Test-DLLPicklePackageArtifact.ps1 -Strict')) + $RuntimeJob | Should -Match ([regex]::Escape('tools/Test-DLLPickleTfmAlignment.ps1 -Strict')) + } + + It 'keeps Build gate stable and aggregates both hosted and exact-runtime jobs' { + $BuildWorkflow | Should -Match '(?m)^\s+name: Build gate\s*$' + $BuildWorkflow | Should -Match 'needs: \[changes, runtime-matrix, build, runtime-tests, dependency-change-report\]' + $BuildWorkflow | Should -Match ([regex]::Escape("foreach (`$RequiredJob in @('runtimeMatrix', 'build', 'runtimeTests'))")) + $BuildWorkflow | Should -Match ([regex]::Escape('needs.runtime-matrix.result')) + $BuildWorkflow | Should -Match ([regex]::Escape('runtimeTests =')) + $BuildWorkflow | Should -Match ([regex]::Escape('needs.runtime-tests.result')) + $BuildWorkflow | Should -Match ([regex]::Escape('needs.dependency-change-report.result')) + } + + It 'requires successful profile-matrix generation whenever live upstream evidence is required' { + $UpstreamWorkflow | Should -Match 'needs: \[pr-changes, profile-matrix, profile-evidence\]' + $UpstreamWorkflow | Should -Match ([regex]::Escape("if (`$MatrixResult -ne 'success')")) + $UpstreamWorkflow | Should -Match ([regex]::Escape('Required exact profile matrix generation did not succeed')) + } + + It 'enforces artifact composition and material size growth in the hosted build gate' { + $BuildWorkflow | Should -Match ([regex]::Escape('Test-DLLPicklePackageArtifact.ps1')) + $BuildWorkflow | Should -Match ([regex]::Escape('New-DLLPickleArtifactSizeReport.ps1')) + $BuildWorkflow | Should -Match ([regex]::Escape('package-policy-reports')) + } + + It 'attaches a base-versus-candidate per-TFM report for Dependabot changes' { + $BuildWorkflow | Should -Match ([regex]::Escape('tools/New-DLLPickleDependencyChangeReport.ps1')) + $BuildWorkflow | Should -Match ([regex]::Escape('dependency-change-report.json')) + $BuildWorkflow | Should -Match ([regex]::Escape('BaselineProjectAssetsPath')) + $BuildWorkflow | Should -Match ([regex]::Escape('ScenarioEvidencePath')) + } +} diff --git a/tools/Compare-DLLPickleConflictMatrix.ps1 b/tools/Compare-DLLPickleConflictMatrix.ps1 index 04529a21..8be3b9d1 100644 --- a/tools/Compare-DLLPickleConflictMatrix.ps1 +++ b/tools/Compare-DLLPickleConflictMatrix.ps1 @@ -2,9 +2,10 @@ .SYNOPSIS Diffs two DLLPickle conflict matrices and reports material drift. .DESCRIPTION - Material drift includes new or removed conflicts, version-set changes, contributing-module-set - changes, and ALC-ownership changes. This matches the versions- and contributor-aware fingerprint - emitted by New-DLLPickleConflictMatrix.ps1 and consumed by the required PR gate. + Material drift includes new or removed tracked assemblies and conflicts, version-set changes, + contributing-module-set changes, selected-hash changes, and ALC-ownership changes. This matches + the profile evidence fingerprint emitted by New-DLLPickleConflictMatrix.ps1 and consumed by the + required PR gate. .PARAMETER Baseline The baseline conflict matrix (as produced by New-DLLPickleConflictMatrix.ps1). .PARAMETER Current @@ -20,6 +21,21 @@ param( $ErrorActionPreference = 'Stop' +$BaselineProfileKey = if ($Baseline.PSObject.Properties.Name -contains 'ProfileKey') { [string]$Baseline.ProfileKey } else { $null } +$CurrentProfileKey = if ($Current.PSObject.Properties.Name -contains 'ProfileKey') { [string]$Current.ProfileKey } else { $null } +$BaselineHasProfileKey = -not [string]::IsNullOrWhiteSpace($BaselineProfileKey) +$CurrentHasProfileKey = -not [string]::IsNullOrWhiteSpace($CurrentProfileKey) +if ($BaselineHasProfileKey -ne $CurrentHasProfileKey) { + throw "Cannot compare conflict matrices when only one declares a runtime profile: '$BaselineProfileKey' and '$CurrentProfileKey'." +} +if ( + $BaselineHasProfileKey -and + $CurrentHasProfileKey -and + $BaselineProfileKey -ne $CurrentProfileKey +) { + throw "Cannot compare conflict matrices from different runtime profiles: '$BaselineProfileKey' and '$CurrentProfileKey'." +} + function Test-DLLPickleStringSetEqual { [CmdletBinding()] param( @@ -36,6 +52,37 @@ function Test-DLLPickleStringSetEqual { return @($Difference).Count -eq 0 } +function Get-DLLPickleAssemblyEvidenceSet { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [object]$Assembly, + + [Parameter(Mandatory)] + [string]$PropertyName, + + [Parameter()] + [string]$LegacyPropertyName + ) + + if ($Assembly.PSObject.Properties.Name -contains $PropertyName) { + return @( + $Assembly.$PropertyName | + ForEach-Object { [string]$_ } | + Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | + Sort-Object -Unique + ) + } + if ( + -not [string]::IsNullOrWhiteSpace($LegacyPropertyName) -and + $Assembly.PSObject.Properties.Name -contains $LegacyPropertyName -and + -not [string]::IsNullOrWhiteSpace([string]$Assembly.$LegacyPropertyName) + ) { + return @([string]$Assembly.$LegacyPropertyName) + } + return @() +} + $BaseSurface = @($Baseline.Assemblies | Where-Object Diverges | ForEach-Object Name) $CurrSurface = @($Current.Assemblies | Where-Object Diverges | ForEach-Object Name) @@ -52,9 +99,13 @@ foreach ($Assembly in $Current.Assemblies) { $CurrentByName[[string]$Assembly.Name] = $Assembly } -$CommonConflicts = @($BaseSurface | Where-Object { $_ -in $CurrSurface }) +$BaseAssemblyNames = @($BaseByName.Keys | Sort-Object) +$CurrentAssemblyNames = @($CurrentByName.Keys | Sort-Object) +$NewTrackedAssemblies = @($CurrentAssemblyNames | Where-Object { $_ -notin $BaseAssemblyNames }) +$RemovedTrackedAssemblies = @($BaseAssemblyNames | Where-Object { $_ -notin $CurrentAssemblyNames }) +$CommonAssemblies = @($BaseAssemblyNames | Where-Object { $_ -in $CurrentAssemblyNames }) $VersionChanges = @( - foreach ($Name in $CommonConflicts) { + foreach ($Name in $CommonAssemblies) { $BaselineVersions = @($BaseByName[$Name].Versions | ForEach-Object { [string]$_ } | Sort-Object -Unique) $CurrentVersions = @($CurrentByName[$Name].Versions | ForEach-Object { [string]$_ } | Sort-Object -Unique) if (-not (Test-DLLPickleStringSetEqual -Left $BaselineVersions -Right $CurrentVersions)) { @@ -68,7 +119,7 @@ $VersionChanges = @( ) $ContributorChanges = @( - foreach ($Name in $CommonConflicts) { + foreach ($Name in $CommonAssemblies) { $BaselineContributors = @($BaseByName[$Name].ShippedBy | ForEach-Object { [string]$_ } | Sort-Object -Unique) $CurrentContributors = @($CurrentByName[$Name].ShippedBy | ForEach-Object { [string]$_ } | Sort-Object -Unique) if (-not (Test-DLLPickleStringSetEqual -Left $BaselineContributors -Right $CurrentContributors)) { @@ -81,28 +132,76 @@ $ContributorChanges = @( } ) -$BaseAlc = @{} -foreach ($Assembly in $Baseline.Assemblies) { - $BaseAlc[$Assembly.Name] = [string]$Assembly.AlcOwner -} -$AlcChanges = @($Current.Assemblies | Where-Object { - $BaseAlc.ContainsKey($_.Name) -and $BaseAlc[$_.Name] -ne [string]$_.AlcOwner - } | ForEach-Object Name) +$HashChanges = @( + foreach ($Name in $CommonAssemblies) { + $BaselineHashes = @(Get-DLLPickleAssemblyEvidenceSet -Assembly $BaseByName[$Name] -PropertyName 'Hashes') + $CurrentHashes = @(Get-DLLPickleAssemblyEvidenceSet -Assembly $CurrentByName[$Name] -PropertyName 'Hashes') + if (-not (Test-DLLPickleStringSetEqual -Left $BaselineHashes -Right $CurrentHashes)) { + [PSCustomObject]@{ + Name = $Name + Baseline = $BaselineHashes + Current = $CurrentHashes + } + } + } +) + +$AlcChangeDetails = @( + foreach ($Name in $CommonAssemblies) { + $BaselineAlcOwners = @( + Get-DLLPickleAssemblyEvidenceSet -Assembly $BaseByName[$Name] -PropertyName 'AlcOwners' -LegacyPropertyName 'AlcOwner' + ) + $CurrentAlcOwners = @( + Get-DLLPickleAssemblyEvidenceSet -Assembly $CurrentByName[$Name] -PropertyName 'AlcOwners' -LegacyPropertyName 'AlcOwner' + ) + if (-not (Test-DLLPickleStringSetEqual -Left $BaselineAlcOwners -Right $CurrentAlcOwners)) { + [PSCustomObject]@{ + Name = $Name + Baseline = $BaselineAlcOwners + Current = $CurrentAlcOwners + } + } + } +) +$AlcChanges = @($AlcChangeDetails | ForEach-Object Name) $Findings = [PSCustomObject]@{ - NewConflicts = $NewConflicts - RemovedConflicts = $RemovedConflicts - VersionChanges = $VersionChanges - ContributorChanges = $ContributorChanges - AlcOwnershipChanges = $AlcChanges + NewTrackedAssemblies = $NewTrackedAssemblies + RemovedTrackedAssemblies = $RemovedTrackedAssemblies + NewConflicts = $NewConflicts + RemovedConflicts = $RemovedConflicts + VersionChanges = $VersionChanges + ContributorChanges = $ContributorChanges + HashChanges = $HashChanges + AlcOwnershipChanges = $AlcChanges + AlcOwnershipChangeDetails = $AlcChangeDetails } +$FindingCanonicalText = @( + "profile=$CurrentProfileKey" + "newTracked=$(@($NewTrackedAssemblies | Sort-Object) -join ',')" + "removedTracked=$(@($RemovedTrackedAssemblies | Sort-Object) -join ',')" + "new=$(@($NewConflicts | Sort-Object) -join ',')" + "removed=$(@($RemovedConflicts | Sort-Object) -join ',')" + "versions=$(@($VersionChanges | Sort-Object Name | ForEach-Object { '{0}:{1}>{2}' -f $_.Name, (@($_.Baseline) -join ','), (@($_.Current) -join ',') }) -join ';')" + "contributors=$(@($ContributorChanges | Sort-Object Name | ForEach-Object { '{0}:{1}>{2}' -f $_.Name, (@($_.Baseline) -join ','), (@($_.Current) -join ',') }) -join ';')" + "hashes=$(@($HashChanges | Sort-Object Name | ForEach-Object { '{0}:{1}>{2}' -f $_.Name, (@($_.Baseline) -join ','), (@($_.Current) -join ',') }) -join ';')" + "alc=$(@($AlcChangeDetails | Sort-Object Name | ForEach-Object { '{0}:{1}>{2}' -f $_.Name, (@($_.Baseline) -join ','), (@($_.Current) -join ',') }) -join ';')" +) -join '|' +$FindingFingerprintBytes = [System.Text.Encoding]::UTF8.GetBytes($FindingCanonicalText) +$FindingFingerprint = [System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::HashData($FindingFingerprintBytes)).Replace('-', '').ToLowerInvariant() + [PSCustomObject]@{ + ProfileKey = $CurrentProfileKey + FindingFingerprint = $FindingFingerprint HasMaterialDrift = ( + $NewTrackedAssemblies.Count -gt 0 -or + $RemovedTrackedAssemblies.Count -gt 0 -or $NewConflicts.Count -gt 0 -or $RemovedConflicts.Count -gt 0 -or $VersionChanges.Count -gt 0 -or $ContributorChanges.Count -gt 0 -or + $HashChanges.Count -gt 0 -or $AlcChanges.Count -gt 0 ) Findings = $Findings diff --git a/tools/DLLPickle.ManualAuthenticatedEvidence.ps1 b/tools/DLLPickle.ManualAuthenticatedEvidence.ps1 new file mode 100644 index 00000000..7cbdae9f --- /dev/null +++ b/tools/DLLPickle.ManualAuthenticatedEvidence.ps1 @@ -0,0 +1,334 @@ +. (Join-Path $PSScriptRoot 'DLLPickle.ProfileEvidence.ps1') + +function ConvertTo-DLLPickleCollapsedAssetPath { + <# + .SYNOPSIS + Collapses a normalized relative asset path without permitting root escape. + + .PARAMETER Path + Forward- or backslash-delimited relative path. + + .OUTPUTS + System.String + #> + + [CmdletBinding()] + [OutputType([string])] + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Path + ) + + $Segments = [System.Collections.Generic.List[string]]::new() + foreach ($Segment in @($Path.Replace('\', '/') -split '/')) { + if ([string]::IsNullOrWhiteSpace($Segment) -or $Segment -eq '.') { + continue + } + if ($Segment -eq '..') { + if ($Segments.Count -eq 0) { + throw "Asset path '$Path' escapes its root." + } + $Segments.RemoveAt($Segments.Count - 1) + continue + } + $Segments.Add($Segment) + } + $Segments -join '/' +} + +function ConvertTo-DLLPickleManualEvidencePath { + <# + .SYNOPSIS + Replaces an authenticated-evidence path root with a stable identifier. + + .PARAMETER Path + Path to normalize. + + .PARAMETER ModuleCacheRoot + Prepared upstream module-cache root. + + .PARAMETER DLLPickleRoot + Prepared DLLPickle module root. + + .PARAMETER RuntimeRoot + Exact PowerShell runtime root. + + .OUTPUTS + System.String + #> + + [CmdletBinding()] + [OutputType([string])] + param ( + [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Path, + [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$ModuleCacheRoot, + [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$DLLPickleRoot, + [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$RuntimeRoot + ) + + $NormalizedPath = $Path.Replace('\', '/').TrimEnd('/') + $Roots = [ordered]@{ + upstream = $ModuleCacheRoot.Replace('\', '/').TrimEnd('/') + dllpickle = $DLLPickleRoot.Replace('\', '/').TrimEnd('/') + runtime = $RuntimeRoot.Replace('\', '/').TrimEnd('/') + } + foreach ($RootEntry in $Roots.GetEnumerator()) { + if ($NormalizedPath -eq $RootEntry.Value) { + return "$($RootEntry.Key):." + } + if ($NormalizedPath.StartsWith("$($RootEntry.Value)/", [System.StringComparison]::OrdinalIgnoreCase)) { + $Relative = $NormalizedPath.Substring($RootEntry.Value.Length + 1) + return '{0}:{1}' -f $RootEntry.Key, (ConvertTo-DLLPickleCollapsedAssetPath -Path $Relative) + } + } + throw "Authenticated evidence path '$Path' is outside the upstream, DLLPickle, and exact runtime roots." +} + +function ConvertTo-DLLPickleUpstreamManifestIdentifier { + <# + .SYNOPSIS + Converts a prepared module manifest path to an upstream evidence identifier. + + .PARAMETER ManifestPath + Absolute selected module manifest path. + + .PARAMETER ModuleCachePath + Absolute prepared module-cache root. + + .OUTPUTS + System.String + #> + + [CmdletBinding()] + [OutputType([string])] + param ( + [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$ManifestPath, + [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$ModuleCachePath + ) + + $NormalizedManifest = $ManifestPath.Replace('\', '/').TrimEnd('/') + $NormalizedRoot = $ModuleCachePath.Replace('\', '/').TrimEnd('/') + if (-not $NormalizedManifest.StartsWith("$NormalizedRoot/", [System.StringComparison]::OrdinalIgnoreCase)) { + throw "Module manifest '$ManifestPath' is outside its prepared module cache." + } + $Relative = ConvertTo-DLLPickleCollapsedAssetPath -Path $NormalizedManifest.Substring($NormalizedRoot.Length + 1) + 'upstream:{0}' -f $Relative +} + +function Get-DLLPicklePreparedInventoryFingerprint { + <# + .SYNOPSIS + Fingerprints the exact selected profile and every prepared module file. + + .PARAMETER Inventory + Upstream inventory whose selected modules must be bound to checkpoints. + + .OUTPUTS + System.String + #> + + [CmdletBinding()] + [OutputType([string])] + param ( + [Parameter(Mandatory)] + [ValidateNotNull()] + [object]$Inventory + ) + + $ModuleCacheRoot = [System.IO.Path]::GetFullPath([string]$Inventory.ModuleCachePath) + if (-not (Test-Path -LiteralPath $ModuleCacheRoot -PathType Container)) { + throw "Prepared module cache was not found: $ModuleCacheRoot" + } + $CanonicalRows = [System.Collections.Generic.List[string]]::new() + $CanonicalRows.Add('schemaVersion=1') + $ProfileRow = 'profile|{0}|{1}|{2}|{3}|{4}|{5}' -f @( + [string]$Inventory.Profile.PowerShellVersion, + [string]$Inventory.Profile.PowerShellLine, + [string]$Inventory.Profile.TargetFramework, + [string]$Inventory.Profile.Platform, + [string]$Inventory.Profile.Architecture, + [string]$Inventory.ProfileKey + ) + $CanonicalRows.Add($ProfileRow) + + $Modules = @(Get-DLLPickleOrdinalSequence -InputObject @($Inventory.Modules) -KeySelector { param($Module) [string]$Module.Name }) + if ($Modules.Count -eq 0) { + throw 'Prepared inventory contains no selected modules.' + } + $SeenModuleNames = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::Ordinal) + foreach ($Module in $Modules) { + if (-not $SeenModuleNames.Add([string]$Module.Name)) { + throw "Prepared inventory contains duplicate module '$($Module.Name)'." + } + $ModulePath = [System.IO.Path]::GetFullPath([string]$Module.ModulePath) + $RelativeModulePath = [System.IO.Path]::GetRelativePath($ModuleCacheRoot, $ModulePath).Replace('\', '/') + if ([System.IO.Path]::IsPathRooted($RelativeModulePath) -or + $RelativeModulePath -eq '..' -or + $RelativeModulePath.StartsWith('../', [System.StringComparison]::Ordinal)) { + throw "Prepared module '$($Module.Name)' is outside its module cache." + } + if (-not (Test-Path -LiteralPath $ModulePath -PathType Container)) { + throw "Prepared module '$($Module.Name)' was not found: $ModulePath" + } + $ModuleManifestPath = [System.IO.Path]::GetFullPath([string]$Module.ModuleManifestPath) + $RelativeManifestPath = [System.IO.Path]::GetRelativePath($ModulePath, $ModuleManifestPath).Replace('\', '/') + if ([System.IO.Path]::IsPathRooted($RelativeManifestPath) -or + $RelativeManifestPath -eq '..' -or + $RelativeManifestPath.StartsWith('../', [System.StringComparison]::Ordinal) -or + -not (Test-Path -LiteralPath $ModuleManifestPath -PathType Leaf)) { + throw "Prepared module '$($Module.Name)' manifest is outside its selected module root." + } + $ModuleRow = 'module|{0}|{1}|{2}|{3}|{4}' -f @( + [string]$Module.Name, + [string]$Module.Version, + [string]$Module.LatestCompatibleVersion, + (ConvertTo-DLLPickleCollapsedAssetPath -Path $RelativeModulePath), + (ConvertTo-DLLPickleCollapsedAssetPath -Path $RelativeManifestPath) + ) + $CanonicalRows.Add($ModuleRow) + + $ModuleEntries = @(Get-ChildItem -LiteralPath $ModulePath -Force -Recurse) + foreach ($ModuleEntry in $ModuleEntries) { + if (($ModuleEntry.Attributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0) { + throw "Prepared module input must not be a symbolic link or reparse point: $($ModuleEntry.FullName)" + } + } + $ModuleFileRows = @( + foreach ($ModuleFile in @($ModuleEntries | Where-Object { -not $_.PSIsContainer })) { + [pscustomobject]@{ + RelativePath = [System.IO.Path]::GetRelativePath($ModulePath, $ModuleFile.FullName).Replace('\', '/') + FullName = [string]$ModuleFile.FullName + Length = [long]$ModuleFile.Length + Attributes = $ModuleFile.Attributes + } + } + ) + $ModuleFiles = @( + Get-DLLPickleOrdinalSequence -InputObject $ModuleFileRows -KeySelector { param($File) [string]$File.RelativePath } -Unique + ) + if ($ModuleFiles.Count -eq 0) { + throw "Prepared module '$($Module.Name)' contains no files." + } + foreach ($ModuleFile in $ModuleFiles) { + $RelativeFilePath = [string]$ModuleFile.RelativePath + $FileHash = (Get-FileHash -LiteralPath $ModuleFile.FullName -Algorithm SHA256).Hash.ToLowerInvariant() + $FileRow = 'file|{0}|{1}|{2}|{3}' -f @( + [string]$Module.Name, + $RelativeFilePath, + $FileHash, + [long]$ModuleFile.Length + ) + $CanonicalRows.Add($FileRow) + } + } + + $CanonicalText = $CanonicalRows -join [char]10 + $Bytes = [System.Text.Encoding]::UTF8.GetBytes($CanonicalText) + [System.BitConverter]::ToString( + [System.Security.Cryptography.SHA256]::HashData($Bytes) + ).Replace('-', '').ToLowerInvariant() +} + +function Get-DLLPickleAuthenticatedCommand { + <# + .SYNOPSIS + Resolves one hard-coded authenticated harness command from its exact module. + + .PARAMETER Name + Command name. + + .PARAMETER Module + Exact prepared module that must own the command. + + .OUTPUTS + System.Management.Automation.CommandInfo + #> + + [CmdletBinding()] + [OutputType([System.Management.Automation.CommandInfo])] + param ( + [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Name, + [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Module + ) + + $Commands = @(Get-Command -Name $Name -Module $Module -CommandType Function, Cmdlet -ErrorAction Stop) + if ($Commands.Count -ne 1) { + throw "Expected exactly one '$Name' command from prepared module '$Module'; found $($Commands.Count)." + } + $Commands[0] +} + +function Invoke-DLLPickleAuthenticatedReadProbe { + <# + .SYNOPSIS + Runs one hard-coded authenticated read probe and returns a sanitized result. + + .PARAMETER ProbeId + Fixed probe identifier. No command text is accepted. + + .OUTPUTS + System.Collections.Specialized.OrderedDictionary + #> + + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$ProbeId + ) + + $Stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + try { + switch ($ProbeId) { + 'graph-context' { + $Command = Get-DLLPickleAuthenticatedCommand -Name 'Get-MgContext' -Module 'Microsoft.Graph.Authentication' + if (-not (& $Command -ErrorAction Stop)) { throw 'Graph context was not established.' } + } + 'graph-me-read' { + $Command = Get-DLLPickleAuthenticatedCommand -Name 'Invoke-MgGraphRequest' -Module 'Microsoft.Graph.Authentication' + & $Command -Method GET -Uri '/v1.0/me?$select=id' -ErrorAction Stop | Out-Null + } + 'exo-mailbox-read' { + $Command = Get-DLLPickleAuthenticatedCommand -Name 'Get-EXOMailbox' -Module 'ExchangeOnlineManagement' + if (@(& $Command -ResultSize 1 -ErrorAction Stop).Count -eq 0) { throw 'Exchange mailbox read returned no object.' } + } + 'az-context' { + $Command = Get-DLLPickleAuthenticatedCommand -Name 'Get-AzContext' -Module 'Az.Accounts' + if (-not (& $Command -ErrorAction Stop)) { throw 'Azure context was not established.' } + } + 'az-resource-read' { + $Command = Get-DLLPickleAuthenticatedCommand -Name 'Get-AzResource' -Module 'Az.Resources' + if (@(& $Command -ErrorAction Stop | Select-Object -First 1).Count -eq 0) { throw 'Azure resource read returned no object.' } + } + 'az-storage-account-read' { + $Command = Get-DLLPickleAuthenticatedCommand -Name 'Get-AzStorageAccount' -Module 'Az.Storage' + if (@(& $Command -ErrorAction Stop | Select-Object -First 1).Count -eq 0) { throw 'Azure storage-account read returned no object.' } + } + 'teams-tenant-read' { + $Command = Get-DLLPickleAuthenticatedCommand -Name 'Get-CsTenant' -Module 'MicrosoftTeams' + if (-not (& $Command -ErrorAction Stop)) { throw 'Teams tenant read returned no object.' } + } + default { throw "Unsupported authenticated probe identifier '$ProbeId'." } + } + $Stopwatch.Stop() + [ordered]@{ + probeId = $ProbeId + executed = $true + status = 'passed' + durationMilliseconds = [long]$Stopwatch.ElapsedMilliseconds + writesPerformed = $false + errorType = $null + } + } catch { + $Stopwatch.Stop() + [ordered]@{ + probeId = $ProbeId + executed = $true + status = 'failed' + durationMilliseconds = [long]$Stopwatch.ElapsedMilliseconds + writesPerformed = $false + errorType = $_.Exception.GetType().FullName + } + } +} diff --git a/tools/DLLPickle.ProfileEvidence.ps1 b/tools/DLLPickle.ProfileEvidence.ps1 new file mode 100644 index 00000000..cf6891ab --- /dev/null +++ b/tools/DLLPickle.ProfileEvidence.ps1 @@ -0,0 +1,133 @@ +function Get-DLLPickleNormalizedEvidenceFingerprint { + <# + .SYNOPSIS + Recomputes the canonical fingerprint for normalized profile evidence. + + .PARAMETER Evidence + A normalized evidence envelope with schemaVersion 1 and fingerprinted content. + + .OUTPUTS + System.String + #> + + [CmdletBinding()] + [OutputType([string])] + param ( + [Parameter(Mandatory)] + [object]$Evidence + ) + + if ([int]$Evidence.schemaVersion -ne 1) { + throw 'Normalized profile evidence has an unsupported schema.' + } + + $ContentProperty = $Evidence.PSObject.Properties['content'] + if ($null -eq $ContentProperty -or $null -eq $ContentProperty.Value) { + throw 'Normalized profile evidence has no fingerprinted content.' + } + + $CanonicalContent = $ContentProperty.Value | ConvertTo-Json -Depth 100 -Compress + $Bytes = [System.Text.Encoding]::UTF8.GetBytes($CanonicalContent) + [System.BitConverter]::ToString( + [System.Security.Cryptography.SHA256]::HashData($Bytes) + ).Replace('-', '').ToLowerInvariant() +} + +function ConvertTo-DLLPickleUtcDateTimeOffset { + <# + .SYNOPSIS + Converts a date/time value to a UTC DateTimeOffset. + + .PARAMETER Value + DateTimeOffset, DateTime, or invariant date/time text to normalize. + + .OUTPUTS + System.DateTimeOffset + #> + + [CmdletBinding()] + [OutputType([System.DateTimeOffset])] + param ( + [Parameter(Mandatory)] + [object]$Value + ) + + if ($Value -is [System.DateTimeOffset]) { + return $Value.ToUniversalTime() + } + if ($Value -is [System.DateTime]) { + return ([System.DateTimeOffset]$Value).ToUniversalTime() + } + [System.DateTimeOffset]::Parse( + [string]$Value, + [System.Globalization.CultureInfo]::InvariantCulture, + [System.Globalization.DateTimeStyles]::AssumeUniversal + ).ToUniversalTime() +} + +function Get-DLLPickleOrdinalSequence { + <# + .SYNOPSIS + Sorts values deterministically with ordinal string comparison. + + .PARAMETER InputObject + Values to sort. + + .PARAMETER KeySelector + Optional script block that returns the primary string sort key for each value. + + .PARAMETER Unique + Return only the first value for each ordinal primary key. + + .OUTPUTS + System.Object + #> + + [CmdletBinding()] + [OutputType([object])] + param ( + [Parameter(Mandatory)] + [AllowNull()] + [AllowEmptyCollection()] + [object[]]$InputObject, + + [Parameter()] + [scriptblock]$KeySelector = { param($Item) [string]$Item }, + + [Parameter()] + [switch]$Unique + ) + + $SortableValues = @($InputObject | Where-Object { $null -ne $_ }) + if ($SortableValues.Count -eq 0) { + return + } + + $Entries = [System.Collections.Generic.List[object]]::new() + foreach ($Value in $SortableValues) { + $PrimaryKey = [string](& $KeySelector $Value) + $TieBreaker = $Value | ConvertTo-Json -Depth 100 -Compress + $Entries.Add([pscustomobject]@{ + PrimaryKey = $PrimaryKey + SortKey = $PrimaryKey + [char]0 + $TieBreaker + Value = $Value + }) + } + $Entries.Sort([System.Comparison[object]]{ + param($Left, $Right) + [System.StringComparer]::Ordinal.Compare([string]$Left.SortKey, [string]$Right.SortKey) + }) + + $PreviousKey = $null + $HasPreviousKey = $false + foreach ($Entry in $Entries) { + if ($Unique -and $HasPreviousKey -and + [System.StringComparer]::Ordinal.Equals($PreviousKey, [string]$Entry.PrimaryKey)) { + continue + } + + $Entry.Value + $PreviousKey = [string]$Entry.PrimaryKey + $HasPreviousKey = $true + } +} diff --git a/tools/Get-DLLPickleBundleSourceFingerprint.ps1 b/tools/Get-DLLPickleBundleSourceFingerprint.ps1 new file mode 100644 index 00000000..f3c8579d --- /dev/null +++ b/tools/Get-DLLPickleBundleSourceFingerprint.ps1 @@ -0,0 +1,112 @@ +<# +.SYNOPSIS +Computes a deterministic fingerprint of every published-bundle source input. + +.DESCRIPTION +Hashes module sources, the locked build project, and every repository input that +controls PrepareModuleOutput: the build file, settings, tooling bootstrap, pinned +tool policy, build entry point, and SDK selection. Repository-root paths and +timestamps are excluded. The result can bind transitional manual authenticated +evidence to bundle content even though committing that evidence necessarily +changes the Git commit SHA. + +.PARAMETER RepositoryRoot +Repository root containing src/DLLPickle and src/DLLPickle.Build. + +.PARAMETER OutputPath +Optional JSON report path. + +.OUTPUTS +System.Management.Automation.PSCustomObject +#> + +[CmdletBinding()] +[OutputType([pscustomobject])] +param ( + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$RepositoryRoot = (Split-Path -Path $PSScriptRoot -Parent), + + [Parameter()] + [string]$OutputPath +) + +$ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'DLLPickle.ProfileEvidence.ps1') +$ResolvedRepositoryRoot = (Resolve-Path -LiteralPath $RepositoryRoot).Path +$ModuleSourceRoot = Join-Path $ResolvedRepositoryRoot 'src/DLLPickle' +if (-not (Test-Path -LiteralPath $ModuleSourceRoot -PathType Container)) { + throw "Published module source directory was not found: $ModuleSourceRoot" +} +$RequiredRelativePaths = @( + 'build/DLLPickle.Build.ps1' + 'build/DLLPickle.Settings.ps1' + 'build/DLLPickle.Tooling.ps1' + 'build/build-tool-versions.json' + 'global.json' + 'src/DLLPickle.Build/DLLPickle.csproj' + 'src/DLLPickle.Build/packages.lock.json' + 'tools/Invoke-DLLPickleBuild.ps1' +) +$RequiredFiles = @( + foreach ($RequiredRelativePath in $RequiredRelativePaths) { + Join-Path $ResolvedRepositoryRoot $RequiredRelativePath + } +) +foreach ($RequiredFile in $RequiredFiles) { + if (-not (Test-Path -LiteralPath $RequiredFile -PathType Leaf)) { + throw "Published bundle input was not found: $RequiredFile" + } +} + +$SourceFiles = @( + Get-ChildItem -LiteralPath $ModuleSourceRoot -File -Recurse + foreach ($RequiredFile in $RequiredFiles) { + Get-Item -LiteralPath $RequiredFile + } +) +if ($SourceFiles.Count -eq 0) { + throw 'No published bundle source inputs were found.' +} + +$Rows = @( + foreach ($SourceFile in $SourceFiles) { + if (($SourceFile.Attributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0) { + throw "Published bundle input must not be a symbolic link or reparse point: $($SourceFile.FullName)" + } + $RelativePath = [System.IO.Path]::GetRelativePath($ResolvedRepositoryRoot, $SourceFile.FullName).Replace('\', '/') + if ($RelativePath.StartsWith('../', [System.StringComparison]::Ordinal)) { + throw "Published bundle input escaped the repository root: $($SourceFile.FullName)" + } + $ContentBytes = [System.IO.File]::ReadAllBytes($SourceFile.FullName) + $Sha256 = [System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::HashData($ContentBytes)).Replace('-', '').ToLowerInvariant() + [pscustomobject][ordered]@{ + path = $RelativePath + sha256 = $Sha256 + length = [long]$SourceFile.Length + } + } +) +$Rows = @( + Get-DLLPickleOrdinalSequence -InputObject $Rows -KeySelector { param($Row) [string]$Row.path } -Unique +) +$CanonicalRows = @( + 'schemaVersion=1' + $Rows | ForEach-Object { '{0}|{1}|{2}' -f $_.path, $_.sha256, $_.length } +) -join [char]10 +$FingerprintBytes = [System.Text.Encoding]::UTF8.GetBytes($CanonicalRows) +$Fingerprint = [System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::HashData($FingerprintBytes)).Replace('-', '').ToLowerInvariant() +$Report = [pscustomobject][ordered]@{ + schemaVersion = 1 + fingerprint = $Fingerprint + files = $Rows +} + +if (-not [string]::IsNullOrWhiteSpace($OutputPath)) { + $OutputDirectory = Split-Path -Path $OutputPath -Parent + if ($OutputDirectory -and -not (Test-Path -LiteralPath $OutputDirectory -PathType Container)) { + $null = New-Item -Path $OutputDirectory -ItemType Directory -Force + } + $Report | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $OutputPath -Encoding utf8NoBOM +} +$Report diff --git a/tools/Get-DLLPickleLoadedTrackedAssembly.ps1 b/tools/Get-DLLPickleLoadedTrackedAssembly.ps1 index 68f0bc37..1e50bcf1 100644 --- a/tools/Get-DLLPickleLoadedTrackedAssembly.ps1 +++ b/tools/Get-DLLPickleLoadedTrackedAssembly.ps1 @@ -17,7 +17,7 @@ .PARAMETER NameLike Optional wildcard patterns; when supplied, an assembly must ALSO match one of them to be returned. .OUTPUTS - PSCustomObject[] with Name, Version, Alc, Path. Sorted by Name. + PSCustomObject[] with Name, Version, ALC, path, hash, OS, platform, and architecture. Sorted by Name. #> [CmdletBinding()] param( @@ -35,6 +35,23 @@ if (-not $PolicyPath) { } $TrackedNames = @((Get-Content -LiteralPath $PolicyPath -Raw | ConvertFrom-Json).trackedAssemblies) +$Platform = if ([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::Windows)) { + 'windows' +} elseif ([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::OSX)) { + 'macos' +} elseif ([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::Linux)) { + 'linux' +} else { + 'unknown' +} +$OperatingSystemDescription = [System.Runtime.InteropServices.RuntimeInformation]::OSDescription +if ($Platform -eq 'macos') { + $MacOSProductVersion = (& /usr/bin/sw_vers -productVersion).Trim() + if ($LASTEXITCODE -ne 0 -or $MacOSProductVersion -notmatch '^\d+\.\d+(?:\.\d+)?$') { + throw "Could not normalize the macOS product version returned by sw_vers: '$MacOSProductVersion'." + } + $OperatingSystemDescription = "macOS $MacOSProductVersion" +} [System.AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $TrackedNames -contains $_.GetName().Name } | @@ -48,11 +65,22 @@ $TrackedNames = @((Get-Content -LiteralPath $PolicyPath -Raw | ConvertFrom-Json) } | ForEach-Object { $Alc = [System.Runtime.Loader.AssemblyLoadContext]::GetLoadContext($_) + $AssemblyPath = $_.Location [PSCustomObject]@{ - Name = $_.GetName().Name - Version = $_.GetName().Version.ToString() - Alc = if ($Alc -and $Alc.Name) { $Alc.Name } else { 'Default' } - Path = $_.Location + Name = $_.GetName().Name + Version = $_.GetName().Version.ToString() + FullName = $_.FullName + Alc = if ($Alc -and $Alc.Name) { $Alc.Name } else { 'Default' } + IsCollectible = if ($Alc) { $Alc.IsCollectible } else { $false } + Path = $AssemblyPath + Sha256 = if (-not [string]::IsNullOrWhiteSpace($AssemblyPath) -and (Test-Path -LiteralPath $AssemblyPath -PathType Leaf)) { + (Get-FileHash -LiteralPath $AssemblyPath -Algorithm SHA256).Hash.ToLowerInvariant() + } else { + $null + } + OS = $OperatingSystemDescription + Platform = $Platform + Architecture = [System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant() } } | Sort-Object Name diff --git a/tools/Get-DLLPicklePowerShellSupportUpdate.ps1 b/tools/Get-DLLPicklePowerShellSupportUpdate.ps1 new file mode 100644 index 00000000..78bde536 --- /dev/null +++ b/tools/Get-DLLPicklePowerShellSupportUpdate.ps1 @@ -0,0 +1,320 @@ +<# +.SYNOPSIS + Discovers PowerShell servicing and support-contract update candidates. + +.DESCRIPTION + Compares the canonical exact test matrix with stable releases from the official + PowerShell GitHub repository. Patch candidates include the required platform + archives and GitHub-published SHA-256 digests when available. New GA minor lines, + approaching retirement, and expired lines are reported separately. The tool is + read-only; publishing a proposal is a distinct workflow action. Stable patch and + support-contract fingerprints allow that workflow to suppress duplicate PRs, + issues, and comments without using timestamps as publication identities. + +.PARAMETER ReleaseDataPath + Optional JSON fixture or captured GitHub releases response. When omitted, query + the official PowerShell/PowerShell releases API. + +.PARAMETER LifecycleDataPath + Optional captured Microsoft Lifecycle HTML fixture. When omitted, query the + official Microsoft PowerShell lifecycle page. + +.PARAMETER RequireCurrent + Throw if a newer patch, new GA line, expired line, or incomplete checksum set is found. +#> + +[CmdletBinding()] +param( + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$TestMatrixPath = (Join-Path (Split-Path -Parent $PSScriptRoot) 'build/powershell-test-matrix.json'), + + [Parameter()] + [string]$ReleaseDataPath, + + [Parameter()] + [string]$LifecycleDataPath, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$OutputPath = (Join-Path (Split-Path -Parent $PSScriptRoot) 'artifacts/lifecycle/powershell-support-update.json'), + + [Parameter()] + [datetime]$AsOfUtc = [datetime]::UtcNow, + + [Parameter()] + [switch]$RequireCurrent +) + +$ErrorActionPreference = 'Stop' + +function Get-DLLPickleStableFingerprint { + [CmdletBinding()] + [OutputType([string])] + param( + [Parameter()] + [AllowEmptyCollection()] + [string[]]$CanonicalLine = @() + ) + + $CanonicalText = @($CanonicalLine | Sort-Object) -join [char]10 + $Bytes = [System.Text.Encoding]::UTF8.GetBytes($CanonicalText) + return [System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::HashData($Bytes)).Replace('-', '').ToLowerInvariant() +} + +if (-not (Test-Path -LiteralPath $TestMatrixPath -PathType Leaf)) { + throw "PowerShell test matrix was not found: $TestMatrixPath" +} + +$TestMatrix = Get-Content -LiteralPath $TestMatrixPath -Raw | ConvertFrom-Json -ErrorAction Stop +$ReleaseData = if (-not [string]::IsNullOrWhiteSpace($ReleaseDataPath)) { + if (-not (Test-Path -LiteralPath $ReleaseDataPath -PathType Leaf)) { + throw "PowerShell release data was not found: $ReleaseDataPath" + } + @(Get-Content -LiteralPath $ReleaseDataPath -Raw | ConvertFrom-Json -ErrorAction Stop) +} else { + $Headers = @{ + Accept = 'application/vnd.github+json' + 'X-GitHub-Api-Version' = '2022-11-28' + 'User-Agent' = 'DLLPickle-support-policy' + } + @(Invoke-RestMethod -Uri 'https://api.github.com/repos/PowerShell/PowerShell/releases?per_page=100' -Headers $Headers -ErrorAction Stop) +} + +$StableReleases = @( + foreach ($Release in $ReleaseData) { + $TagMatch = [regex]::Match([string]$Release.tag_name, '^v(?7\.\d+\.\d+)$') + if (-not $Release.draft -and -not $Release.prerelease -and $TagMatch.Success) { + $ReleaseVersion = [version]$TagMatch.Groups['version'].Value + $PublishedAt = if ($Release.published_at) { [datetime]$Release.published_at } else { [datetime]::MinValue } + if ($PublishedAt -le $AsOfUtc.ToUniversalTime()) { + [PSCustomObject]@{ + Version = $ReleaseVersion + ReleaseLine = '{0}.{1}' -f $ReleaseVersion.Major, $ReleaseVersion.Minor + PublishedAt = $PublishedAt.ToUniversalTime() + HtmlUrl = [string]$Release.html_url + Assets = @($Release.assets) + } + } + } + } +) +if ($StableReleases.Count -eq 0) { + throw 'No stable PowerShell 7 release records were found in the official release data.' +} + +$LifecycleContent = if (-not [string]::IsNullOrWhiteSpace($LifecycleDataPath)) { + if (-not (Test-Path -LiteralPath $LifecycleDataPath -PathType Leaf)) { + throw "PowerShell lifecycle data was not found: $LifecycleDataPath" + } + Get-Content -LiteralPath $LifecycleDataPath -Raw +} else { + (Invoke-WebRequest -Uri ([string]$TestMatrix.lifecycleSourceUrl) -UseBasicParsing -ErrorAction Stop).Content +} +$PacificTimeZone = try { + [System.TimeZoneInfo]::FindSystemTimeZoneById('America/Los_Angeles') +} catch { + [System.TimeZoneInfo]::FindSystemTimeZoneById('Pacific Standard Time') +} +$LiveLifecycleRows = @( + foreach ($TableRow in [regex]::Matches($LifecycleContent, '(?is)]*>(?.*?)')) { + $VersionMatch = [regex]::Match($TableRow.Groups['content'].Value, '(?is)]*>\s*PowerShell\s+(?7\.\d+)(?:\s*\(LTS\))?\s*') + $DateMatches = [regex]::Matches($TableRow.Groups['content'].Value, '(?is)]*\sdatetime="(?[^"]+)"') + if (-not $VersionMatch.Success -or $DateMatches.Count -ne 2) { + continue + } + $StartUtc = [datetime]::SpecifyKind([datetime]::Parse($DateMatches[0].Groups['date'].Value, [System.Globalization.CultureInfo]::InvariantCulture), [System.DateTimeKind]::Utc) + $EndUtc = [datetime]::SpecifyKind([datetime]::Parse($DateMatches[1].Groups['date'].Value, [System.Globalization.CultureInfo]::InvariantCulture), [System.DateTimeKind]::Utc) + [PSCustomObject]@{ + ReleaseLine = $VersionMatch.Groups['line'].Value + StartDate = [System.TimeZoneInfo]::ConvertTimeFromUtc($StartUtc, $PacificTimeZone).ToString('yyyy-MM-dd') + EndDate = [System.TimeZoneInfo]::ConvertTimeFromUtc($EndUtc, $PacificTimeZone).ToString('yyyy-MM-dd') + } + } +) +if ($LiveLifecycleRows.Count -eq 0) { + throw 'No PowerShell 7 release rows were parsed from the official Microsoft lifecycle data.' +} +$DuplicateLifecycleLines = @($LiveLifecycleRows | Group-Object ReleaseLine | Where-Object Count -NE 1) +if ($DuplicateLifecycleLines.Count -gt 0) { + throw "Microsoft lifecycle data contains duplicate PowerShell lines: $($DuplicateLifecycleLines.Name -join ', ')." +} +$AsOfPacificDate = [System.TimeZoneInfo]::ConvertTimeFromUtc($AsOfUtc.ToUniversalTime(), $PacificTimeZone).Date +$SupportedLifecycleLines = @($LiveLifecycleRows | Where-Object { + $StartDate = [datetime]::ParseExact( + [string]$_.StartDate, + 'yyyy-MM-dd', + [System.Globalization.CultureInfo]::InvariantCulture + ) + $EndDate = [datetime]::ParseExact( + [string]$_.EndDate, + 'yyyy-MM-dd', + [System.Globalization.CultureInfo]::InvariantCulture + ) + $StartDate -le $AsOfPacificDate -and $EndDate -ge $AsOfPacificDate + } | ForEach-Object ReleaseLine) + +$DeclaredLines = @($TestMatrix.profiles | ForEach-Object { '{0}.{1}' -f $_.powerShellMajor, $_.powerShellMinor }) +$PatchUpdates = @( + foreach ($MatrixProfile in @($TestMatrix.profiles)) { + $ReleaseLine = '{0}.{1}' -f $MatrixProfile.powerShellMajor, $MatrixProfile.powerShellMinor + $NewestRelease = $StableReleases | Where-Object ReleaseLine -EQ $ReleaseLine | Sort-Object Version -Descending | Select-Object -First 1 + if (-not $NewestRelease -or $NewestRelease.Version -le [version]$MatrixProfile.powerShellVersion) { + continue + } + + $ArchiveCandidates = @( + foreach ($CurrentArchive in @($TestMatrix.archiveAssets | Where-Object powerShellVersion -EQ $MatrixProfile.powerShellVersion)) { + $ExpectedFileName = ([string]$CurrentArchive.fileName).Replace([string]$MatrixProfile.powerShellVersion, $NewestRelease.Version.ToString()) + $ReleaseAsset = @($NewestRelease.Assets | Where-Object name -EQ $ExpectedFileName | Select-Object -First 1)[0] + $Digest = if ($ReleaseAsset -and [string]$ReleaseAsset.digest -match '^sha256:(?[a-fA-F0-9]{64})$') { + $Matches['hash'].ToLowerInvariant() + } else { + $null + } + [PSCustomObject]@{ + Platform = [string]$CurrentArchive.platform + Architecture = [string]$CurrentArchive.architecture + FileName = $ExpectedFileName + DownloadUrl = if ($ReleaseAsset) { [string]$ReleaseAsset.browser_download_url } else { $null } + Sha256 = $Digest + Complete = $null -ne $ReleaseAsset -and -not [string]::IsNullOrWhiteSpace($Digest) + } + } + ) + [PSCustomObject]@{ + ReleaseLine = $ReleaseLine + CurrentVersion = [string]$MatrixProfile.powerShellVersion + CandidateVersion = $NewestRelease.Version.ToString() + ReleaseUrl = $NewestRelease.HtmlUrl + Archives = @($ArchiveCandidates) + ChecksumsComplete = @($ArchiveCandidates | Where-Object { -not $_.Complete }).Count -eq 0 + } + } +) + +$MaximumDeclaredMinor = @($TestMatrix.profiles.powerShellMinor | Measure-Object -Maximum)[0].Maximum +$NewLines = @( + $StableReleases | + Where-Object { $_.Version.Minor -gt $MaximumDeclaredMinor -and $_.ReleaseLine -notin $DeclaredLines -and $_.ReleaseLine -in $SupportedLifecycleLines } | + Group-Object ReleaseLine | + ForEach-Object { $_.Group | Sort-Object Version -Descending | Select-Object -First 1 } | + Sort-Object Version | + ForEach-Object { + [PSCustomObject]@{ + ReleaseLine = $_.ReleaseLine + LatestVersion = $_.Version.ToString() + PublishedAtUtc = $_.PublishedAt.ToString('o') + ReleaseUrl = $_.HtmlUrl + LifecycleEndDate = [string]@($LiveLifecycleRows | Where-Object ReleaseLine -EQ $_.ReleaseLine)[0].EndDate + ProposedMapping = [PSCustomObject]@{ + DotNetMajor = $null + TargetFramework = $null + Status = 'pending checksum-verified runtime identity and maintainer support-contract review' + } + PackageSizeEstimate = 'not-run-pending-reviewed-mapping' + BuildResults = 'not-run-pending-reviewed-mapping' + InitialUpstreamConflictEvidence = 'not-run-pending-reviewed-mapping' + RequiredDecision = 'Map PowerShell line to CLR/TFM, estimate artifact size, and collect initial profile-aware conflict evidence.' + } + } +) + +$LifecycleRows = @( + foreach ($MatrixProfile in @($TestMatrix.profiles)) { + $ReleaseLine = '{0}.{1}' -f $MatrixProfile.powerShellMajor, $MatrixProfile.powerShellMinor + $LiveLifecycle = @($LiveLifecycleRows | Where-Object ReleaseLine -EQ $ReleaseLine) + if ($LiveLifecycle.Count -ne 1) { + continue + } + $EndDate = [datetime]::ParseExact([string]$LiveLifecycle[0].EndDate, 'yyyy-MM-dd', [System.Globalization.CultureInfo]::InvariantCulture) + $DaysRemaining = [math]::Floor(($EndDate.Date.AddDays(1) - $AsOfPacificDate).TotalDays) + [PSCustomObject]@{ + ReleaseLine = $ReleaseLine + LifecycleEnd = [string]$LiveLifecycle[0].EndDate + MatrixLifecycleEnd = [string]$MatrixProfile.lifecycleEndDate + DaysRemaining = $DaysRemaining + Status = if ($DaysRemaining -le 0) { 'Expired' } elseif ($DaysRemaining -le [int]$TestMatrix.retirementWarningDays) { 'RetiringSoon' } else { 'Supported' } + } + } +) + +$IncompletePatchUpdates = @($PatchUpdates | Where-Object { -not $_.ChecksumsComplete }) +$LifecycleMissingLines = @($DeclaredLines | Where-Object { $_ -notin $LiveLifecycleRows.ReleaseLine }) +$LifecycleDateChanges = @($LifecycleRows | Where-Object { $_.LifecycleEnd -ne $_.MatrixLifecycleEnd }) +$UndeclaredSupportedLines = @($SupportedLifecycleLines | Where-Object { $_ -notin $DeclaredLines }) +$SupportContractReviewRequired = + $NewLines.Count -gt 0 -or + $UndeclaredSupportedLines.Count -gt 0 -or + $LifecycleDateChanges.Count -gt 0 -or + $LifecycleMissingLines.Count -gt 0 -or + @($LifecycleRows | Where-Object Status -IN @('RetiringSoon', 'Expired')).Count -gt 0 +$PatchCanonicalLines = @( + foreach ($PatchUpdate in @($PatchUpdates | Sort-Object ReleaseLine)) { + 'patch|{0}|{1}|{2}' -f $PatchUpdate.ReleaseLine, $PatchUpdate.CurrentVersion, $PatchUpdate.CandidateVersion + foreach ($Archive in @($PatchUpdate.Archives | Sort-Object Platform, Architecture)) { + 'archive|{0}|{1}|{2}|{3}|{4}' -f $PatchUpdate.CandidateVersion, $Archive.Platform, $Archive.Architecture, $Archive.FileName, $Archive.Sha256 + } + } +) +$SupportContractCanonicalLines = @( + foreach ($NewLine in @($NewLines | Sort-Object ReleaseLine)) { + 'new-line|{0}|{1}|{2}' -f $NewLine.ReleaseLine, $NewLine.LatestVersion, $NewLine.LifecycleEndDate + } + foreach ($LifecycleChange in @($LifecycleDateChanges | Sort-Object ReleaseLine)) { + 'lifecycle-date|{0}|{1}|{2}' -f $LifecycleChange.ReleaseLine, $LifecycleChange.MatrixLifecycleEnd, $LifecycleChange.LifecycleEnd + } + foreach ($MissingLine in @($LifecycleMissingLines | Sort-Object)) { + 'lifecycle-missing|{0}' -f $MissingLine + } + foreach ($UndeclaredLine in @($UndeclaredSupportedLines | Sort-Object)) { + 'undeclared-supported|{0}' -f $UndeclaredLine + } + foreach ($LifecycleState in @($LifecycleRows | Where-Object Status -IN @('RetiringSoon', 'Expired') | Sort-Object ReleaseLine)) { + 'lifecycle-state|{0}|{1}|{2}' -f $LifecycleState.ReleaseLine, $LifecycleState.Status, $LifecycleState.LifecycleEnd + } +) +$PatchProposalFingerprint = Get-DLLPickleStableFingerprint -CanonicalLine $PatchCanonicalLines +$SupportContractFingerprint = Get-DLLPickleStableFingerprint -CanonicalLine $SupportContractCanonicalLines +$Report = [PSCustomObject]@{ + SchemaVersion = 1 + GeneratedAtUtc = [System.DateTimeOffset]::UtcNow.ToString('o') + ReleaseSource = 'https://api.github.com/repos/PowerShell/PowerShell/releases' + LifecycleSource = [string]$TestMatrix.lifecycleSourceUrl + PatchUpdates = @($PatchUpdates) + NewLines = @($NewLines) + Lifecycle = @($LifecycleRows) + LifecycleDateChanges = @($LifecycleDateChanges) + LifecycleMissingLines = @($LifecycleMissingLines) + UndeclaredSupportedLines = @($UndeclaredSupportedLines) + PatchProposalFingerprint = $PatchProposalFingerprint + PatchProposalMarker = '' -f $PatchProposalFingerprint + SupportContractFingerprint = $SupportContractFingerprint + SupportContractMarker = '' -f $SupportContractFingerprint + MatrixOnlyUpdateAvailable = $PatchUpdates.Count -gt 0 -and $IncompletePatchUpdates.Count -eq 0 -and -not $SupportContractReviewRequired + SupportContractReviewRequired = $SupportContractReviewRequired + ProposalPublishingStatus = 'pending-workflow-publication' +} + +$OutputDirectory = Split-Path -Path $OutputPath -Parent +if ($OutputDirectory -and -not (Test-Path -LiteralPath $OutputDirectory -PathType Container)) { + $null = New-Item -Path $OutputDirectory -ItemType Directory -Force +} +$Report | ConvertTo-Json -Depth 30 | Set-Content -LiteralPath $OutputPath -Encoding UTF8 + +if ($RequireCurrent.IsPresent) { + $Violations = [System.Collections.Generic.List[string]]::new() + if ($PatchUpdates.Count -gt 0) { $Violations.Add("newer servicing patches: $($PatchUpdates.CandidateVersion -join ', ')") } + if ($NewLines.Count -gt 0) { $Violations.Add("new GA PowerShell lines: $($NewLines.ReleaseLine -join ', ')") } + if ($UndeclaredSupportedLines.Count -gt 0) { $Violations.Add("undeclared Microsoft-supported lines: $($UndeclaredSupportedLines -join ', ')") } + if ($LifecycleDateChanges.Count -gt 0) { $Violations.Add("lifecycle date changes: $($LifecycleDateChanges.ReleaseLine -join ', ')") } + if ($LifecycleMissingLines.Count -gt 0) { $Violations.Add("declared lines missing from lifecycle data: $($LifecycleMissingLines -join ', ')") } + $ExpiredLines = @($LifecycleRows | Where-Object Status -EQ 'Expired') + if ($ExpiredLines.Count -gt 0) { $Violations.Add("expired lines: $($ExpiredLines.ReleaseLine -join ', ')") } + if ($IncompletePatchUpdates.Count -gt 0) { $Violations.Add('candidate patch assets lack a complete official checksum set') } + if ($Violations.Count -gt 0) { + throw "PowerShell support matrix is not release-current: $($Violations -join '; ')." + } +} + +$Report diff --git a/tools/Get-DLLPickleRuntimeAssemblySnapshot.ps1 b/tools/Get-DLLPickleRuntimeAssemblySnapshot.ps1 index 61e0a699..6619b0c6 100644 --- a/tools/Get-DLLPickleRuntimeAssemblySnapshot.ps1 +++ b/tools/Get-DLLPickleRuntimeAssemblySnapshot.ps1 @@ -19,6 +19,17 @@ .PARAMETER Strict Fails when a requested module cannot be imported or the probe command throws. Use this mode when collecting adjudication evidence so a partial snapshot cannot be mistaken for a successful probe. +.PARAMETER PowerShellExecutable + Exact stock pwsh/pwsh.exe to launch. Defaults to the current process executable. +.PARAMETER PowerShellVersion + Optional exact servicing patch expected from the child process. +.PARAMETER TargetFramework + Expected TFM for the child CLR. The probe fails if it does not match. +.PARAMETER ModuleManifestPath + Optional exact manifest path for each ModuleName, in the same order. This prevents + a user- or machine-wide module of the same name from satisfying the evidence run. +.PARAMETER ModuleSearchPath + Optional isolated module roots assigned inside the fresh child process before import. .OUTPUTS PSCustomObject[] one row per loaded tracked assembly: Name, Version, Alc, Path. #> @@ -36,11 +47,43 @@ param( [Parameter()] [string]$PolicyPath, + [Parameter()] + [string]$PowerShellExecutable = [Environment]::ProcessPath, + + [Parameter()] + [version]$PowerShellVersion, + + [Parameter()] + [ValidatePattern('^net\d+\.0$')] + [string]$TargetFramework, + + [Parameter()] + [string[]]$ModuleManifestPath, + + [Parameter()] + [string[]]$ModuleSearchPath, + [Parameter()] [switch]$Strict ) $ErrorActionPreference = 'Stop' +$ResolvedModuleManifestPaths = @($ModuleManifestPath | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) +if ($ResolvedModuleManifestPaths.Count -gt 0 -and $ResolvedModuleManifestPaths.Count -ne $ModuleName.Count) { + throw 'ModuleManifestPath must contain one exact path for every ModuleName.' +} +foreach ($ManifestPath in $ResolvedModuleManifestPaths) { + if (-not (Test-Path -LiteralPath $ManifestPath -PathType Leaf)) { + throw "Module manifest was not found: $ManifestPath" + } +} + +$ExecutableCommand = Get-Command -Name $PowerShellExecutable -ErrorAction Stop +$ResolvedPowerShellExecutable = if ($ExecutableCommand.CommandType -eq 'Application') { + $ExecutableCommand.Source +} else { + throw "PowerShellExecutable must resolve to an application, not $($ExecutableCommand.CommandType): $PowerShellExecutable" +} $HelperScript = Join-Path -Path $PSScriptRoot -ChildPath 'Get-DLLPickleLoadedTrackedAssembly.ps1' if (-not $PolicyPath) { @@ -48,24 +91,47 @@ if (-not $PolicyPath) { } $ChildScript = @' -param($ModuleNames, $PreloadManifest, $ProbeCommand, $HelperScript, $PolicyPath, [switch]$StrictMode) +param($ModuleNames, $ModuleManifestPathsEncoded, $IsolatedModulePath, $PreloadManifest, $ProbeCommand, $HelperScript, $PolicyPath, $ResultPath, $ExpectedPowerShellVersion, $ExpectedTargetFramework, [switch]$StrictMode) $ModuleNames = $ModuleNames -split ',' +$ModuleManifestPaths = if ($ModuleManifestPathsEncoded) { + $ManifestJson = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($ModuleManifestPathsEncoded)) + @($ManifestJson | ConvertFrom-Json) +} else { + @() +} +$ModuleManifestPaths = @($ModuleManifestPaths) +$env:PSModulePath = if ($IsolatedModulePath) { $IsolatedModulePath } else { $env:PSModulePath } $ErrorActionPreference = 'Continue' +$ActualTargetFramework = 'net{0}.0' -f [Environment]::Version.Major +if ($ExpectedPowerShellVersion -and $PSVersionTable.PSVersion.ToString() -ne $ExpectedPowerShellVersion) { + throw "PowerShell version mismatch. Expected $ExpectedPowerShellVersion but detected $($PSVersionTable.PSVersion)." +} +if ($ExpectedTargetFramework -and $ActualTargetFramework -ne $ExpectedTargetFramework) { + throw "Target framework mismatch. Expected $ExpectedTargetFramework but detected $ActualTargetFramework." +} if ($PreloadManifest) { if ($StrictMode) { Import-Module $PreloadManifest -Force -ErrorAction Stop - Import-DPLibrary -SuppressLogo -ErrorAction Stop | Out-Null + $ImportResults = @(Import-DPLibrary -SuppressLogo -ErrorAction Stop) + $FailedImports = @($ImportResults | Where-Object { [string]$_.Status -eq 'Failed' }) + if ($FailedImports.Count -gt 0) { + throw "DLLPickle preload reported $($FailedImports.Count) failed assembly load(s)." + } } else { Import-Module $PreloadManifest -Force Import-DPLibrary -SuppressLogo | Out-Null } } -foreach ($Name in $ModuleNames) { +$ImportedModulePaths = @() +for ($ModuleIndex = 0; $ModuleIndex -lt $ModuleNames.Count; $ModuleIndex++) { + $Name = $ModuleNames[$ModuleIndex] + $ImportTarget = if ($ModuleManifestPaths.Count -gt 0) { [string]$ModuleManifestPaths[$ModuleIndex] } else { $Name } if ($StrictMode) { - Import-Module $Name -Force -ErrorAction Stop + Import-Module -Name $ImportTarget -Force -ErrorAction Stop } else { - Import-Module $Name -Force -ErrorAction Continue + Import-Module -Name $ImportTarget -Force -ErrorAction Continue } + $ImportedModulePaths += $ImportTarget } if ($ProbeCommand) { if ($StrictMode) { @@ -74,33 +140,63 @@ if ($ProbeCommand) { try { Invoke-Expression $ProbeCommand | Out-Null } catch { } } } -& $HelperScript -PolicyPath $PolicyPath | ConvertTo-Json -Depth 5 +$Rows = @(& $HelperScript -PolicyPath $PolicyPath) +foreach ($Row in $Rows) { + $Row | Add-Member -NotePropertyName PowerShellVersion -NotePropertyValue $PSVersionTable.PSVersion.ToString() + $Row | Add-Member -NotePropertyName DotNetVersion -NotePropertyValue ([Environment]::Version.ToString()) + $Row | Add-Member -NotePropertyName TargetFramework -NotePropertyValue $ActualTargetFramework + $Row | Add-Member -NotePropertyName ExecutablePath -NotePropertyValue ([Environment]::ProcessPath) + $Row | Add-Member -NotePropertyName PSHome -NotePropertyValue $PSHOME + $Row | Add-Member -NotePropertyName ModuleSet -NotePropertyValue @($ModuleNames) + $Row | Add-Member -NotePropertyName ImportOrder -NotePropertyValue @($ModuleNames) + $Row | Add-Member -NotePropertyName ImportedModulePaths -NotePropertyValue @($ImportedModulePaths) + $Row | Add-Member -NotePropertyName IsolatedModulePath -NotePropertyValue $env:PSModulePath + $Row | Add-Member -NotePropertyName DllPicklePreloaded -NotePropertyValue (-not [string]::IsNullOrWhiteSpace($PreloadManifest)) +} +ConvertTo-Json -InputObject @($Rows) -Depth 8 | Set-Content -LiteralPath $ResultPath -Encoding utf8NoBOM '@ -$TempScript = Join-Path ([System.IO.Path]::GetTempPath()) ("dpp-snap-{0}.ps1" -f ([System.Guid]::NewGuid().ToString('n'))) +$TempId = [System.Guid]::NewGuid().ToString('n') +$TempScript = Join-Path ([System.IO.Path]::GetTempPath()) ("dpp-snap-{0}.ps1" -f $TempId) +$TempResult = Join-Path ([System.IO.Path]::GetTempPath()) ("dpp-snap-{0}.json" -f $TempId) Set-Content -LiteralPath $TempScript -Value $ChildScript -Encoding utf8NoBOM try { $ChildArguments = @( '-NoProfile', '-NonInteractive', '-File', $TempScript, '-ModuleNames', ($ModuleName -join ','), '-HelperScript', $HelperScript, - '-PolicyPath', $PolicyPath + '-PolicyPath', $PolicyPath, + '-ResultPath', $TempResult ) + if ($PowerShellVersion) { $ChildArguments += @('-ExpectedPowerShellVersion', $PowerShellVersion.ToString()) } + if ($TargetFramework) { $ChildArguments += @('-ExpectedTargetFramework', $TargetFramework) } + if ($ResolvedModuleManifestPaths.Count -gt 0) { + $ManifestJson = ConvertTo-Json -InputObject @($ResolvedModuleManifestPaths) -Compress + $ManifestEncoded = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($ManifestJson)) + $ChildArguments += @('-ModuleManifestPathsEncoded', $ManifestEncoded) + } + if ($ModuleSearchPath.Count -gt 0) { + $ChildArguments += @('-IsolatedModulePath', (@($ModuleSearchPath) -join [System.IO.Path]::PathSeparator)) + } if ($PreloadDllPickleManifest) { $ChildArguments += @('-PreloadManifest', $PreloadDllPickleManifest) } if ($ProbeCommand) { $ChildArguments += @('-ProbeCommand', $ProbeCommand) } if ($Strict.IsPresent) { $ChildArguments += '-StrictMode' - $Raw = & pwsh @ChildArguments 2>&1 + $Raw = & $ResolvedPowerShellExecutable @ChildArguments 2>&1 if ($LASTEXITCODE -ne 0) { $ChildError = ($Raw | Out-String).Trim() throw "DLLPickle runtime assembly snapshot failed in strict mode. $ChildError" } } else { - $Raw = & pwsh @ChildArguments + $Raw = & $ResolvedPowerShellExecutable @ChildArguments + } + if (-not (Test-Path -LiteralPath $TempResult -PathType Leaf)) { + throw 'DLLPickle runtime assembly snapshot did not produce its result file.' } - $Json = ($Raw | Out-String).Trim() + $Json = (Get-Content -LiteralPath $TempResult -Raw).Trim() if ([string]::IsNullOrWhiteSpace($Json)) { return @() } @($Json | ConvertFrom-Json) } finally { Remove-Item -LiteralPath $TempScript -Force -ErrorAction SilentlyContinue + Remove-Item -LiteralPath $TempResult -Force -ErrorAction SilentlyContinue } diff --git a/tools/Get-DLLPickleUpstreamInventory.ps1 b/tools/Get-DLLPickleUpstreamInventory.ps1 index 4deb0efa..62ff8363 100644 --- a/tools/Get-DLLPickleUpstreamInventory.ps1 +++ b/tools/Get-DLLPickleUpstreamInventory.ps1 @@ -3,10 +3,11 @@ Builds an assembly inventory for upstream PowerShell modules. .DESCRIPTION - Reads build/dependency-policy.json, downloads the latest monitored modules - from PSGallery unless SkipDownload is used, inventories bundled DLL assembly - identities, and writes a structured JSON report for CI/CD compatibility - checks. + Reads build/dependency-policy.json, resolves the newest monitored module release + compatible with the exact tested PowerShell line, and launches that explicit stock + executable to capture only the tracked assembly assets actually selected at runtime. + The report records the PowerShell/CLR/TFM/OS profile, umbrella and constituent module, + assembly identity, hash, path, and load context. .PARAMETER PolicyPath Path to the dependency policy JSON file. @@ -27,6 +28,12 @@ .PARAMETER Force Removes any existing saved copy before downloading a module. +.PARAMETER PowerShellExecutable + Exact stock pwsh/pwsh.exe used for runtime asset selection. + +.PARAMETER TestMatrixPath + Canonical exact servicing-patch and profile matrix. + .EXAMPLE ./tools/Get-DLLPickleUpstreamInventory.ps1 -OutputPath ./artifacts/upstream/inventory.json @@ -38,15 +45,15 @@ param( [Parameter()] [ValidateNotNullOrEmpty()] - [string]$PolicyPath = (Join-Path -Path (Split-Path -Parent $PSScriptRoot) -ChildPath 'build\dependency-policy.json'), + [string]$PolicyPath = (Join-Path -Path (Split-Path -Parent $PSScriptRoot) -ChildPath 'build/dependency-policy.json'), [Parameter()] [ValidateNotNullOrEmpty()] - [string]$OutputPath = (Join-Path -Path (Split-Path -Parent $PSScriptRoot) -ChildPath 'artifacts\upstreamCompatibility\upstream-inventory.json'), + [string]$OutputPath = (Join-Path -Path (Split-Path -Parent $PSScriptRoot) -ChildPath 'artifacts/upstreamCompatibility/upstream-inventory.json'), [Parameter()] [ValidateNotNullOrEmpty()] - [string]$ModuleCachePath = (Join-Path -Path (Split-Path -Parent $PSScriptRoot) -ChildPath 'artifacts\upstreamCompatibility\modules'), + [string]$ModuleCachePath = (Join-Path -Path (Split-Path -Parent $PSScriptRoot) -ChildPath 'artifacts/upstreamCompatibility/modules'), [Parameter()] [ValidateNotNullOrEmpty()] @@ -56,7 +63,15 @@ param( [switch]$SkipDownload, [Parameter()] - [switch]$Force + [switch]$Force, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$PowerShellExecutable = [Environment]::ProcessPath, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$TestMatrixPath = (Join-Path -Path (Split-Path -Parent $PSScriptRoot) -ChildPath 'build/powershell-test-matrix.json') ) $ErrorActionPreference = 'Stop' @@ -102,44 +117,60 @@ function Get-DLLPickleLatestModulePath { Select-Object -First 1 } -function Get-DLLPickleAssemblyInventory { +function Get-DLLPickleRuntimeIdentity { [CmdletBinding()] param( [Parameter(Mandatory)] - [string]$ModulePath, - - [Parameter(Mandatory)] - [string[]]$TrackedAssembly + [string]$ExecutablePath ) - $TrackedLookup = @{} - foreach ($AssemblyName in $TrackedAssembly) { - $TrackedLookup[$AssemblyName] = $true + $Probe = @' +$Platform = if ([Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([Runtime.InteropServices.OSPlatform]::Windows)) { + 'windows' +} elseif ([Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([Runtime.InteropServices.OSPlatform]::OSX)) { + 'macos' +} else { + 'linux' +} +[ordered]@{ + powerShellVersion = $PSVersionTable.PSVersion.ToString() + dotNetVersion = [Environment]::Version.ToString() + dotNetMajor = [Environment]::Version.Major + executablePath = [Environment]::ProcessPath + psHome = $PSHOME + platform = $Platform + architecture = [Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant() +} | ConvertTo-Json -Compress +'@ + $Raw = @(& $ExecutablePath -NoLogo -NoProfile -NonInteractive -Command $Probe 2>&1) + if ($LASTEXITCODE -ne 0) { + throw "PowerShell runtime identity probe failed: $($Raw -join [Environment]::NewLine)" } - - Get-ChildItem -LiteralPath $ModulePath -Filter '*.dll' -File -Recurse | - ForEach-Object { - try { - $AssemblyName = [System.Reflection.AssemblyName]::GetAssemblyName($_.FullName) - [PSCustomObject]@{ - Name = $AssemblyName.Name - Version = $AssemblyName.Version.ToString() - PackageVersionCandidate = ConvertTo-DLLPicklePackageVersion -AssemblyVersion $AssemblyName.Version - FullName = $AssemblyName.FullName - RelativePath = $_.FullName.Substring($ModulePath.Length).TrimStart('\', '/') - Path = $_.FullName - IsTracked = [bool]$TrackedLookup[$AssemblyName.Name] - } - } catch [System.BadImageFormatException] { - Write-Verbose "Skipping non-.NET DLL '$($_.FullName)'." - } - } | - Sort-Object -Property Name, Version, RelativePath + $Raw -join [Environment]::NewLine | ConvertFrom-Json -ErrorAction Stop } $ResolvedPolicyPath = (Resolve-Path -LiteralPath $PolicyPath).Path $Policy = Get-Content -LiteralPath $ResolvedPolicyPath -Raw | ConvertFrom-Json -$TrackedAssemblies = @($Policy.trackedAssemblies | ForEach-Object { [string]$_ }) +$ResolvedMatrixPath = (Resolve-Path -LiteralPath $TestMatrixPath).Path +$TestMatrix = Get-Content -LiteralPath $ResolvedMatrixPath -Raw | ConvertFrom-Json +$ExecutableCommand = Get-Command -Name $PowerShellExecutable -ErrorAction Stop +if ($ExecutableCommand.CommandType -ne 'Application') { + throw "PowerShellExecutable must resolve to an application: $PowerShellExecutable" +} +$ResolvedPowerShellExecutable = $ExecutableCommand.Source +$RuntimeIdentity = Get-DLLPickleRuntimeIdentity -ExecutablePath $ResolvedPowerShellExecutable +$RuntimeProfiles = @($TestMatrix.profiles | Where-Object powerShellVersion -eq $RuntimeIdentity.powerShellVersion) +if ($RuntimeProfiles.Count -ne 1) { + throw "Runtime PowerShell $($RuntimeIdentity.powerShellVersion) is not an exact, unique test-matrix profile." +} +$RuntimeProfile = $RuntimeProfiles[0] +if ([int]$RuntimeIdentity.dotNetMajor -ne [int]$RuntimeProfile.dotnetMajor) { + throw "Runtime CLR mismatch for PowerShell $($RuntimeIdentity.powerShellVersion). Expected CLR $($RuntimeProfile.dotnetMajor), detected CLR $($RuntimeIdentity.dotNetMajor)." +} +$ProfileKey = 'ps{0}-{1}-{2}-{3}' -f ( + '{0}.{1}' -f $RuntimeProfile.powerShellMajor, $RuntimeProfile.powerShellMinor +), $RuntimeProfile.targetFramework, $RuntimeIdentity.platform, $RuntimeIdentity.architecture +$SnapshotScriptPath = Join-Path -Path $PSScriptRoot -ChildPath 'Get-DLLPickleRuntimeAssemblySnapshot.ps1' $PolicyModules = @($Policy.monitoredModules) if ($ModuleName) { $Requested = @{} @@ -161,21 +192,35 @@ if (-not $SkipDownload.IsPresent) { foreach ($PolicyModule in $PolicyModules) { $Name = [string]$PolicyModule.name $Repository = if ($PolicyModule.repository) { [string]$PolicyModule.repository } else { 'PSGallery' } - $GalleryModule = Find-Module -Name $Name -Repository $Repository -ErrorAction Stop + $GalleryModules = @(Find-Module -Name $Name -Repository $Repository -AllVersions -ErrorAction Stop) + $GalleryModule = $GalleryModules | + Where-Object { + -not $_.PowerShellVersion -or [version]$_.PowerShellVersion -le [version]$RuntimeIdentity.powerShellVersion + } | + Sort-Object -Property { [version]([string]$_.Version) } -Descending | + Select-Object -First 1 + if (-not $GalleryModule) { + throw "No release of module '$Name' declares compatibility with PowerShell $($RuntimeIdentity.powerShellVersion)." + } $ResolvedModuleVersions[$Name] = $GalleryModule.Version } -} - -$ModuleResults = foreach ($PolicyModule in $PolicyModules) { - $Name = [string]$PolicyModule.name - $Repository = if ($PolicyModule.repository) { [string]$PolicyModule.repository } else { 'PSGallery' } - if (-not $SkipDownload.IsPresent) { - $ModuleRoot = Join-Path -Path $ModuleCachePath -ChildPath $Name - if ($Force.IsPresent -and (Test-Path -LiteralPath $ModuleRoot)) { - Remove-Item -LiteralPath $ModuleRoot -Recurse -Force + # Prepare the complete cache before taking any runtime snapshot. A monitored module can save + # another monitored module as a dependency, so removing/replacing roots during the snapshot + # loop could otherwise make earlier rows describe a transient cache state. + if ($Force.IsPresent) { + foreach ($PolicyModule in $PolicyModules) { + $Name = [string]$PolicyModule.name + $ModuleRoot = Join-Path -Path $ModuleCachePath -ChildPath $Name + if (Test-Path -LiteralPath $ModuleRoot) { + Remove-Item -LiteralPath $ModuleRoot -Recurse -Force + } } + } + foreach ($PolicyModule in $PolicyModules) { + $Name = [string]$PolicyModule.name + $Repository = if ($PolicyModule.repository) { [string]$PolicyModule.repository } else { 'PSGallery' } $SaveModuleParameters = @{ Name = $Name RequiredVersion = $ResolvedModuleVersions[$Name] @@ -189,28 +234,141 @@ $ModuleResults = foreach ($PolicyModule in $PolicyModules) { } Save-Module @SaveModuleParameters } +} - $SavedModule = Get-DLLPickleLatestModulePath -RootPath $ModuleCachePath -Name $Name +$ModuleResults = foreach ($PolicyModule in $PolicyModules) { + $Name = [string]$PolicyModule.name + $Repository = if ($PolicyModule.repository) { [string]$PolicyModule.repository } else { 'PSGallery' } + + $SavedModule = if ($SkipDownload.IsPresent) { + Get-DLLPickleLatestModulePath -RootPath $ModuleCachePath -Name $Name + } else { + $ResolvedModulePath = Join-Path -Path $ModuleCachePath -ChildPath ( + [System.IO.Path]::Combine($Name, [string]$ResolvedModuleVersions[$Name]) + ) + Get-Item -LiteralPath $ResolvedModulePath -ErrorAction SilentlyContinue + } if (-not $SavedModule) { throw "Module '$Name' was not found under '$ModuleCachePath'." } - $Assemblies = @(Get-DLLPickleAssemblyInventory -ModulePath $SavedModule.FullName -TrackedAssembly $TrackedAssemblies) + $ModuleManifestPath = Get-ChildItem -LiteralPath $SavedModule.FullName -Filter "$Name.psd1" -File -Recurse | + Sort-Object -Property { $_.FullName.Length } | + Select-Object -First 1 + if (-not $ModuleManifestPath) { + throw "Module manifest '$Name.psd1' was not found under '$($SavedModule.FullName)'." + } + $OriginalPSModulePath = $env:PSModulePath + $Manifest = $null + try { + $SystemModulePath = Join-Path -Path $RuntimeIdentity.psHome -ChildPath 'Modules' + $env:PSModulePath = @($ModuleCachePath, $SystemModulePath) -join [System.IO.Path]::PathSeparator + # Real gallery manifests can contain module-manifest expressions such as a + # PSEdition-dependent RootModule. Import-PowerShellDataFile deliberately rejects + # those expressions; Test-ModuleManifest evaluates the constrained manifest grammar + # and returns the compatibility metadata PowerShell itself uses. Validate only after + # isolating PSModulePath so RequiredModules saved beside the monitored module resolve. + $Manifest = Test-ModuleManifest -Path $ModuleManifestPath.FullName -ErrorAction Stop + + $SnapshotParameters = @{ + ModuleName = @($Name) + ModuleManifestPath = @($ModuleManifestPath.FullName) + ModuleSearchPath = @($ModuleCachePath, $SystemModulePath) + PolicyPath = $ResolvedPolicyPath + PowerShellExecutable = $ResolvedPowerShellExecutable + PowerShellVersion = [version]$RuntimeIdentity.powerShellVersion + TargetFramework = [string]$RuntimeProfile.targetFramework + Strict = $true + } + if (-not [string]::IsNullOrWhiteSpace([string]$PolicyModule.deterministicProbeCommand)) { + $SnapshotParameters['ProbeCommand'] = [string]$PolicyModule.deterministicProbeCommand + } + $RuntimeAssemblies = @(& $SnapshotScriptPath @SnapshotParameters) + } finally { + $env:PSModulePath = $OriginalPSModulePath + } + + $Assemblies = @( + foreach ($Assembly in $RuntimeAssemblies) { + $ConstituentModule = $Name + if (-not [string]::IsNullOrWhiteSpace([string]$Assembly.Path)) { + $FullAssemblyPath = [System.IO.Path]::GetFullPath($Assembly.Path) + $FullModuleCachePath = [System.IO.Path]::GetFullPath($ModuleCachePath) + $FullPSHomePath = [System.IO.Path]::GetFullPath($RuntimeIdentity.psHome) + $RelativeToCache = [System.IO.Path]::GetRelativePath( + $FullModuleCachePath, + $FullAssemblyPath + ) + $RelativeToPSHome = [System.IO.Path]::GetRelativePath($FullPSHomePath, $FullAssemblyPath) + $IsWithinModuleCache = -not $RelativeToCache.StartsWith('..', [System.StringComparison]::Ordinal) -and + -not [System.IO.Path]::IsPathRooted($RelativeToCache) + $IsWithinPSHome = -not $RelativeToPSHome.StartsWith('..', [System.StringComparison]::Ordinal) -and + -not [System.IO.Path]::IsPathRooted($RelativeToPSHome) + if (-not $IsWithinModuleCache -and -not $IsWithinPSHome) { + throw "Runtime evidence selected an assembly outside the isolated module cache and exact PSHOME: $FullAssemblyPath" + } + if ($IsWithinModuleCache) { + $ConstituentModule = ($RelativeToCache -split '[\\/]')[0] + } + } + [PSCustomObject]@{ + Name = [string]$Assembly.Name + Version = [string]$Assembly.Version + PackageVersionCandidate = ConvertTo-DLLPicklePackageVersion -AssemblyVersion ([version]$Assembly.Version) + FullName = [string]$Assembly.FullName + Path = [string]$Assembly.Path + SelectedAssetPath = [string]$Assembly.Path + Sha256 = [string]$Assembly.Sha256 + Alc = [string]$Assembly.Alc + IsCollectible = [bool]$Assembly.IsCollectible + ConstituentModule = $ConstituentModule + PowerShellVersion = [string]$Assembly.PowerShellVersion + DotNetVersion = [string]$Assembly.DotNetVersion + TargetFramework = [string]$Assembly.TargetFramework + OS = [string]$Assembly.OS + Platform = [string]$Assembly.Platform + Architecture = [string]$Assembly.Architecture + } + } + ) [PSCustomObject]@{ - Name = $Name - Version = $SavedModule.Name - Repository = $Repository - ModulePath = $SavedModule.FullName - Purpose = [string]$PolicyModule.purpose - Assemblies = $Assemblies - TrackedAssemblies = @($Assemblies | Where-Object IsTracked) + Name = $Name + UmbrellaModule = if ($PolicyModule.umbrellaModule) { [string]$PolicyModule.umbrellaModule } else { $Name } + ConstituentModule = $Name + Version = $SavedModule.Name + LatestCompatibleVersion = $SavedModule.Name + Repository = $Repository + ModulePath = $SavedModule.FullName + ModuleManifestPath = if ($ModuleManifestPath) { $ModuleManifestPath.FullName } else { $null } + ManifestPowerShellVersion = if ($Manifest -and $Manifest.PowerShellVersion) { $Manifest.PowerShellVersion.ToString() } else { $null } + CompatiblePSEditions = if ($Manifest) { @($Manifest.CompatiblePSEditions) } else { @() } + Purpose = [string]$PolicyModule.purpose + DeterministicProbeCommand = [string]$PolicyModule.deterministicProbeCommand + Assemblies = $Assemblies + TrackedAssemblies = $Assemblies } } $Report = [PSCustomObject]@{ + SchemaVersion = 2 GeneratedAtUtc = [System.DateTimeOffset]::UtcNow.ToString('o') PolicyPath = $ResolvedPolicyPath + TestMatrixPath = $ResolvedMatrixPath ModuleCachePath = (Resolve-Path -LiteralPath $ModuleCachePath).Path + ProfileKey = $ProfileKey + ValidationTier = 'DeterministicImportNoAuth' + Profile = [PSCustomObject]@{ + PowerShellVersion = [string]$RuntimeIdentity.powerShellVersion + PowerShellLine = '{0}.{1}' -f $RuntimeProfile.powerShellMajor, $RuntimeProfile.powerShellMinor + DotNetVersion = [string]$RuntimeIdentity.dotNetVersion + DotNetMajor = [int]$RuntimeIdentity.dotNetMajor + TargetFramework = [string]$RuntimeProfile.targetFramework + ExecutablePath = [string]$RuntimeIdentity.executablePath + PSHome = [string]$RuntimeIdentity.psHome + Platform = [string]$RuntimeIdentity.platform + Architecture = [string]$RuntimeIdentity.architecture + } + ModuleSet = @($ModuleResults.Name) Modules = @($ModuleResults) } diff --git a/tools/Initialize-DLLPickleManualAuthenticatedCompatibility.ps1 b/tools/Initialize-DLLPickleManualAuthenticatedCompatibility.ps1 new file mode 100644 index 00000000..9477e946 --- /dev/null +++ b/tools/Initialize-DLLPickleManualAuthenticatedCompatibility.ps1 @@ -0,0 +1,102 @@ +<# +.SYNOPSIS +Prepares exact runtimes, latest compatible modules, and DLLPickle output for manual authentication tests. + +.DESCRIPTION +Performs no authentication and no service calls. It builds the local module, +installs the checksum-pinned Windows x64 PowerShell runtimes from the canonical +matrix, and resolves/downloads the latest compatible monitored modules into an +isolated gitignored work root. +#> + +[CmdletBinding()] +[OutputType([pscustomobject])] +param ( + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$WorkRoot = (Join-Path (Split-Path -Path $PSScriptRoot -Parent) 'artifacts/manual-authenticated'), + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$TestMatrixPath = (Join-Path (Split-Path -Path $PSScriptRoot -Parent) 'build/powershell-test-matrix.json'), + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$PolicyPath = (Join-Path (Split-Path -Path $PSScriptRoot -Parent) 'build/dependency-policy.json') +) + +$ErrorActionPreference = 'Stop' +$RepositoryRoot = Split-Path -Path $PSScriptRoot -Parent +. (Join-Path $PSScriptRoot 'DLLPickle.ManualAuthenticatedEvidence.ps1') +$ResolvedWorkRoot = [System.IO.Path]::GetFullPath($WorkRoot) +$RuntimeInstallRoot = Join-Path $ResolvedWorkRoot 'runtimes' +$ModuleCacheParent = Join-Path $ResolvedWorkRoot 'module-cache' +$InventoryRoot = Join-Path $ResolvedWorkRoot 'inventories' +$DLLPickleManifestPath = Join-Path $RepositoryRoot 'module/DLLPickle/DLLPickle.psd1' + +& (Join-Path $RepositoryRoot 'tools/Invoke-DLLPickleBuild.ps1') -Task PrepareModuleOutput +if (-not (Test-Path -LiteralPath $DLLPickleManifestPath -PathType Leaf)) { + throw "DLLPickle build output was not created: $DLLPickleManifestPath" +} +$Matrix = Get-Content -LiteralPath $TestMatrixPath -Raw | ConvertFrom-Json -ErrorAction Stop +$PreparedProfiles = @( + foreach ($RuntimeProfile in @($Matrix.profiles)) { + $PowerShellLine = '{0}.{1}' -f $RuntimeProfile.powerShellMajor, $RuntimeProfile.powerShellMinor + $ProfileKey = 'ps{0}-{1}-windows-x64' -f $PowerShellLine, $RuntimeProfile.targetFramework + Write-Information -MessageData "Preparing $ProfileKey with PowerShell $($RuntimeProfile.powerShellVersion)..." -InformationAction Continue + $InstallParameters = @{ + Provider = 'DirectArchive' + PowerShellVersion = [string]$RuntimeProfile.powerShellVersion + Platform = 'windows' + Architecture = 'x64' + InstallRoot = $RuntimeInstallRoot + PassThru = $true + } + $Identity = & (Join-Path $RepositoryRoot 'tools/Install-DLLPickleTestPowerShell.ps1') @InstallParameters + $ProfileInventoryRoot = Join-Path $InventoryRoot $ProfileKey + $InventoryPath = Join-Path $ProfileInventoryRoot 'upstream-inventory.json' + $ModuleCachePath = Join-Path $ModuleCacheParent ([string]$RuntimeProfile.powerShellVersion) + $InventoryParameters = @{ + PolicyPath = $PolicyPath + TestMatrixPath = $TestMatrixPath + ModuleCachePath = $ModuleCachePath + OutputPath = $InventoryPath + PowerShellExecutable = [string]$Identity.ExecutablePath + Force = $true + } + $Inventory = & (Join-Path $RepositoryRoot 'tools/Get-DLLPickleUpstreamInventory.ps1') @InventoryParameters + $StaleSelections = @($Inventory.Modules | Where-Object { + [string]$_.Version -ne [string]$_.LatestCompatibleVersion + }) + if ($StaleSelections.Count -gt 0) { + throw "The prepared inventory for '$ProfileKey' did not select every latest compatible module." + } + $InventoryFingerprint = Get-DLLPicklePreparedInventoryFingerprint -Inventory $Inventory + $Inventory | Add-Member -MemberType NoteProperty -Name InventoryFingerprint -Value $InventoryFingerprint -Force + $Inventory | ConvertTo-Json -Depth 100 | Set-Content -LiteralPath $InventoryPath -Encoding utf8NoBOM + $PreparedProfile = [pscustomobject]@{ + ProfileKey = $ProfileKey + PowerShellVersion = [string]$RuntimeProfile.powerShellVersion + TargetFramework = [string]$RuntimeProfile.targetFramework + ExecutablePath = [string]$Identity.ExecutablePath + InventoryPath = $InventoryPath + InventoryFingerprint = $InventoryFingerprint + ModuleVersions = [ordered]@{} + } + foreach ($Module in @(Get-DLLPickleOrdinalSequence -InputObject @($Inventory.Modules) -KeySelector { param($Item) [string]$Item.Name } -Unique)) { + $PreparedProfile.ModuleVersions[[string]$Module.Name] = [string]$Module.Version + } + $PreparedProfile + } +) +$BundleFingerprint = & (Join-Path $RepositoryRoot 'tools/Get-DLLPickleBundleSourceFingerprint.ps1') -RepositoryRoot $RepositoryRoot -OutputPath (Join-Path $ResolvedWorkRoot 'bundle-source-fingerprint.json') +$Summary = [pscustomobject]@{ + WorkRoot = $ResolvedWorkRoot + DLLPickleManifestPath = $DLLPickleManifestPath + BundleSourceFingerprint = [string]$BundleFingerprint.fingerprint + Profiles = $PreparedProfiles + AuthenticationPerformed = $false + WritesPerformed = $false +} +$Summary | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath (Join-Path $ResolvedWorkRoot 'preparation-summary.json') -Encoding utf8NoBOM +$Summary diff --git a/tools/Install-DLLPickleTestPowerShell.ps1 b/tools/Install-DLLPickleTestPowerShell.ps1 new file mode 100644 index 00000000..10042ff8 --- /dev/null +++ b/tools/Install-DLLPickleTestPowerShell.ps1 @@ -0,0 +1,401 @@ +<# +.SYNOPSIS +Provision or validate an exact stock PowerShell executable for DLLPickle tests. + +.DESCRIPTION +Uses either an explicit executable, a checksum-verified official PowerShell archive, +or the pinned optional multi-pwsh CI provider. The returned executable is always the +official payload pwsh/pwsh.exe, never an alias, shim, hosted process, or PATH lookup. +For a lifecycle candidate matrix that explicitly marks validation pending, the +provisioner records the observed .NET patch while still enforcing the declared CLR +major. Authoritative matrices always require an exact .NET runtime-version match. + +.PARAMETER PowerShellExecutable +An already-provisioned stock PowerShell executable to validate and return. + +.PARAMETER Provider +The provisioning provider. DirectArchive downloads the official Microsoft release +archive. MultiPwsh uses the separately pinned CI-only provider. + +.PARAMETER PowerShellVersion +The exact servicing patch required by the CI support matrix. + +.PARAMETER InstallRoot +Runner-temporary installation and cache root. No persistent PATH changes are made. + +.PARAMETER MatrixPath +Path to build/powershell-test-matrix.json. + +.PARAMETER Platform +Target platform. Defaults to the current process platform. + +.PARAMETER Architecture +Target process architecture. Defaults to the current process architecture. + +.PARAMETER PassThru +Return structured runtime identity details instead of only the executable path. + +.OUTPUTS +System.String or System.Management.Automation.PSCustomObject +#> + +[CmdletBinding(DefaultParameterSetName = 'Provider')] +[OutputType([string], [pscustomobject])] +param ( + [Parameter(Mandatory, ParameterSetName = 'Executable')] + [ValidateNotNullOrEmpty()] + [string]$PowerShellExecutable, + + [Parameter(Mandatory, ParameterSetName = 'Provider')] + [ValidateSet('DirectArchive', 'MultiPwsh')] + [string]$Provider, + + [Parameter(Mandatory)] + [version]$PowerShellVersion, + + [Parameter(ParameterSetName = 'Provider')] + [string]$InstallRoot, + + [Parameter()] + [string]$MatrixPath = (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -ChildPath 'build/powershell-test-matrix.json'), + + [Parameter(ParameterSetName = 'Provider')] + [ValidateSet('windows', 'linux', 'macos')] + [string]$Platform, + + [Parameter(ParameterSetName = 'Provider')] + [ValidateSet('x64', 'x86', 'arm64', 'arm32')] + [string]$Architecture, + + [Parameter()] + [switch]$PassThru +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +function Get-CurrentPlatformName { + if ([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::Windows)) { + return 'windows' + } + if ([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::OSX)) { + return 'macos' + } + if ([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::Linux)) { + return 'linux' + } + + throw 'The current operating system is not supported by the DLLPickle test matrix.' +} + +function Get-VerifiedDownload { + param ( + [Parameter(Mandatory)] + [uri]$Uri, + + [Parameter(Mandatory)] + [string]$DestinationPath, + + [Parameter(Mandatory)] + [ValidatePattern('^[a-fA-F0-9]{64}$')] + [string]$Sha256 + ) + + $DestinationDirectory = Split-Path -Path $DestinationPath -Parent + if (-not (Test-Path -LiteralPath $DestinationDirectory -PathType Container)) { + $null = New-Item -Path $DestinationDirectory -ItemType Directory -Force + } + + if (Test-Path -LiteralPath $DestinationPath -PathType Leaf) { + $ExistingHash = (Get-FileHash -LiteralPath $DestinationPath -Algorithm SHA256).Hash + if ($ExistingHash -ieq $Sha256) { + return + } + + Remove-Item -LiteralPath $DestinationPath -Force + } + + $DownloadParameters = @{ + Uri = $Uri + OutFile = $DestinationPath + UseBasicParsing = $true + ConnectionTimeoutSeconds = 60 + OperationTimeoutSeconds = 300 + MaximumRetryCount = 3 + RetryIntervalSec = 5 + } + Invoke-WebRequest @DownloadParameters + $ActualHash = (Get-FileHash -LiteralPath $DestinationPath -Algorithm SHA256).Hash + if ($ActualHash -ine $Sha256) { + Remove-Item -LiteralPath $DestinationPath -Force + throw "Checksum validation failed for '$Uri'. Expected $Sha256 but received $ActualHash." + } +} + +function Expand-TestRuntimeArchive { + param ( + [Parameter(Mandatory)] + [string]$ArchivePath, + + [Parameter(Mandatory)] + [string]$DestinationPath + ) + + if (Test-Path -LiteralPath $DestinationPath) { + Remove-Item -LiteralPath $DestinationPath -Recurse -Force + } + + $StagingPath = '{0}.staging-{1}-{2}' -f $DestinationPath, $PID, ([System.Guid]::NewGuid().ToString('n')) + $null = New-Item -Path $StagingPath -ItemType Directory -Force + try { + if ([System.IO.Path]::GetExtension($ArchivePath) -eq '.zip') { + Expand-Archive -LiteralPath $ArchivePath -DestinationPath $StagingPath -Force + } else { + $TarOutput = @(& tar -xzf $ArchivePath -C $StagingPath 2>&1) + if ($LASTEXITCODE -ne 0) { + throw "Failed to extract '$ArchivePath': $($TarOutput -join [Environment]::NewLine)" + } + } + + Move-Item -LiteralPath $StagingPath -Destination $DestinationPath + } finally { + if (Test-Path -LiteralPath $StagingPath) { + Remove-Item -LiteralPath $StagingPath -Recurse -Force + } + } +} + +function Test-PathWithinRoot { + param ( + [Parameter(Mandatory)] + [string]$Path, + + [Parameter(Mandatory)] + [string]$Root + ) + + $RelativePath = [System.IO.Path]::GetRelativePath( + [System.IO.Path]::GetFullPath($Root), + [System.IO.Path]::GetFullPath($Path) + ) + -not $RelativePath.StartsWith('..', [System.StringComparison]::Ordinal) -and + -not [System.IO.Path]::IsPathRooted($RelativePath) +} + +function Get-StockPowerShellIdentity { + param ( + [Parameter(Mandatory)] + [string]$ExecutablePath + ) + + $IdentityProbe = @' +$Platform = if ([Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([Runtime.InteropServices.OSPlatform]::Windows)) { + 'windows' +} elseif ([Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([Runtime.InteropServices.OSPlatform]::OSX)) { + 'macos' +} elseif ([Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([Runtime.InteropServices.OSPlatform]::Linux)) { + 'linux' +} else { + 'unknown' +} +[ordered]@{ + powerShellVersion = $PSVersionTable.PSVersion.ToString() + dotNetVersion = [Environment]::Version.ToString() + dotNetMajor = [Environment]::Version.Major + psHome = $PSHOME + processPath = [Environment]::ProcessPath + platform = $Platform + architecture = [Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant() +} | ConvertTo-Json -Compress +'@ + + $ProbeOutput = @(& $ExecutablePath -NoLogo -NoProfile -NonInteractive -Command $IdentityProbe 2>&1) + if ($LASTEXITCODE -ne 0) { + throw "Stock PowerShell identity probe failed for '$ExecutablePath': $($ProbeOutput -join [Environment]::NewLine)" + } + + try { + $ProbeOutput -join [Environment]::NewLine | ConvertFrom-Json -ErrorAction Stop + } catch { + throw "Stock PowerShell identity probe returned malformed output for '$ExecutablePath': $($ProbeOutput -join [Environment]::NewLine)" + } +} + +if (-not (Test-Path -LiteralPath $MatrixPath -PathType Leaf)) { + throw "PowerShell test matrix not found: $MatrixPath" +} + +try { + $Matrix = Get-Content -LiteralPath $MatrixPath -Raw | ConvertFrom-Json -ErrorAction Stop +} catch { + throw "PowerShell test matrix is malformed: $($_.Exception.Message)" +} + +$ExactVersion = $PowerShellVersion.ToString() +$Profiles = @($Matrix.profiles | Where-Object powerShellVersion -eq $ExactVersion) +if ($Profiles.Count -ne 1) { + throw "PowerShell $ExactVersion is not an exact, unique servicing patch in the DLLPickle test matrix." +} +$ExpectedProfile = $Profiles[0] +$CandidateValidationPending = ( + $Matrix.PSObject.Properties.Name -contains 'candidateValidationPending' -and + $Matrix.candidateValidationPending -is [bool] -and + $Matrix.candidateValidationPending +) +$ExpectedDotNetRuntimeVersionText = [string]$ExpectedProfile.dotnetRuntimeVersion +$ExpectedDotNetRuntimeVersion = $null +if ([string]::IsNullOrWhiteSpace($ExpectedDotNetRuntimeVersionText)) { + if (-not $CandidateValidationPending) { + throw "PowerShell $ExactVersion has no dotnetRuntimeVersion in the DLLPickle test matrix." + } +} else { + try { + $ExpectedDotNetRuntimeVersion = [version]$ExpectedDotNetRuntimeVersionText + } catch { + throw "PowerShell $ExactVersion has an invalid dotnetRuntimeVersion '$ExpectedDotNetRuntimeVersionText' in the DLLPickle test matrix." + } +} + +$ExpectedPayloadRoot = $null +if ($PSCmdlet.ParameterSetName -eq 'Executable') { + if (-not (Test-Path -LiteralPath $PowerShellExecutable -PathType Leaf)) { + throw "Explicit PowerShell executable not found: $PowerShellExecutable" + } + $ResolvedExecutable = (Resolve-Path -LiteralPath $PowerShellExecutable).Path + $EffectiveProvider = 'ExplicitExecutable' +} else { + if ([string]::IsNullOrWhiteSpace($Platform)) { + $Platform = Get-CurrentPlatformName + } + if ([string]::IsNullOrWhiteSpace($Architecture)) { + $Architecture = [System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant() + } + if ([string]::IsNullOrWhiteSpace($InstallRoot)) { + $TemporaryRoot = if (-not [string]::IsNullOrWhiteSpace($env:RUNNER_TEMP)) { + $env:RUNNER_TEMP + } else { + [System.IO.Path]::GetTempPath() + } + $InstallRoot = Join-Path -Path $TemporaryRoot -ChildPath 'DLLPickle/TestPowerShell' + } + $InstallRoot = [System.IO.Path]::GetFullPath($InstallRoot) + + $Lane = @($Matrix.lanes | Where-Object { $_.platform -eq $Platform -and $_.architecture -eq $Architecture }) + if ($Lane.Count -ne 1) { + throw "The DLLPickle test matrix does not declare exactly one $Platform/$Architecture lane." + } + $ExecutableName = [string]$Lane[0].executableName + + if ($Provider -eq 'DirectArchive') { + $Archive = @($Matrix.archiveAssets | Where-Object { + $_.powerShellVersion -eq $ExactVersion -and $_.platform -eq $Platform -and $_.architecture -eq $Architecture + }) + if ($Archive.Count -ne 1) { + throw "The DLLPickle test matrix does not declare exactly one official archive for PowerShell $ExactVersion on $Platform/$Architecture." + } + + $ProviderRoot = Join-Path -Path $InstallRoot -ChildPath ([System.IO.Path]::Combine('DirectArchive', $ExactVersion, "$Platform-$Architecture")) + $CachePath = Join-Path -Path $ProviderRoot -ChildPath (Join-Path 'cache' $Archive[0].fileName) + $ExpectedPayloadRoot = Join-Path -Path $ProviderRoot -ChildPath 'payload' + $ExpectedExecutable = Join-Path -Path $ExpectedPayloadRoot -ChildPath $ExecutableName + + # Revalidate the immutable archive and recreate the payload on every use. + # Runtime identity alone cannot detect a corrupted or modified cached file. + Get-VerifiedDownload -Uri $Archive[0].downloadUrl -DestinationPath $CachePath -Sha256 $Archive[0].sha256 + Expand-TestRuntimeArchive -ArchivePath $CachePath -DestinationPath $ExpectedPayloadRoot + if ($Platform -ne 'windows') { + $ChmodOutput = @(& chmod u+x $ExpectedExecutable 2>&1) + if ($LASTEXITCODE -ne 0) { + throw "Failed to mark the stock PowerShell executable as executable: $($ChmodOutput -join [Environment]::NewLine)" + } + } + } else { + $OptionalProvider = $Matrix.provisioning.optionalProvider + if (-not $OptionalProvider.ciOnly -or $OptionalProvider.name -ne 'MultiPwsh') { + throw 'The optional provider policy must identify MultiPwsh as CI-only.' + } + $ProviderAsset = @($OptionalProvider.assets | Where-Object { + $_.platform -eq $Platform -and $_.architecture -eq $Architecture + }) + if ($ProviderAsset.Count -ne 1) { + throw "The DLLPickle test matrix does not declare exactly one MultiPwsh asset for $Platform/$Architecture." + } + + $ProviderRoot = Join-Path -Path $InstallRoot -ChildPath ([System.IO.Path]::Combine('MultiPwsh', "v$($OptionalProvider.version)", "$Platform-$Architecture", $ExactVersion)) + $ToolCachePath = Join-Path -Path $ProviderRoot -ChildPath (Join-Path 'cache' $ProviderAsset[0].fileName) + $ToolPayloadRoot = Join-Path -Path $ProviderRoot -ChildPath 'tool' + $ToolExecutableName = if ($Platform -eq 'windows') { 'multi-pwsh.exe' } else { 'multi-pwsh' } + $MultiPwshExecutable = Join-Path -Path $ToolPayloadRoot -ChildPath $ToolExecutableName + $MultiPwshInstallRoot = Join-Path -Path $ProviderRoot -ChildPath 'payload' + $ExpectedPayloadRoot = Join-Path -Path $MultiPwshInstallRoot -ChildPath (Join-Path 'multi' $ExactVersion) + $ExpectedExecutable = Join-Path -Path $ExpectedPayloadRoot -ChildPath $ExecutableName + + if (-not (Test-Path -LiteralPath $ExpectedExecutable -PathType Leaf)) { + if (-not (Test-Path -LiteralPath $MultiPwshExecutable -PathType Leaf)) { + Get-VerifiedDownload -Uri $ProviderAsset[0].downloadUrl -DestinationPath $ToolCachePath -Sha256 $ProviderAsset[0].sha256 + Expand-TestRuntimeArchive -ArchivePath $ToolCachePath -DestinationPath $ToolPayloadRoot + if ($Platform -ne 'windows') { + $ChmodOutput = @(& chmod u+x $MultiPwshExecutable 2>&1) + if ($LASTEXITCODE -ne 0) { + throw "Failed to mark multi-pwsh as executable: $($ChmodOutput -join [Environment]::NewLine)" + } + } + } + + $InstallOutput = @(& $MultiPwshExecutable install $ExactVersion --scope user --root $MultiPwshInstallRoot --arch $Architecture --no-add-path 2>&1) + if ($LASTEXITCODE -ne 0) { + throw "MultiPwsh failed to install PowerShell ${ExactVersion}: $($InstallOutput -join [Environment]::NewLine)" + } + } + } + + if (-not (Test-Path -LiteralPath $ExpectedExecutable -PathType Leaf)) { + throw "$Provider did not produce the expected official PowerShell executable: $ExpectedExecutable" + } + $ResolvedExecutable = (Resolve-Path -LiteralPath $ExpectedExecutable).Path + if (-not (Test-PathWithinRoot -Path $ResolvedExecutable -Root $ExpectedPayloadRoot)) { + throw "Rejected provider executable outside the expected official payload root: $ResolvedExecutable" + } + $EffectiveProvider = $Provider +} + +$Identity = Get-StockPowerShellIdentity -ExecutablePath $ResolvedExecutable +if ([version]$Identity.powerShellVersion -ne $PowerShellVersion) { + throw "PowerShell version mismatch. Expected $ExactVersion but '$ResolvedExecutable' reported $($Identity.powerShellVersion)." +} +if ([int]$Identity.dotNetMajor -ne [int]$ExpectedProfile.dotnetMajor) { + throw "CLR mismatch for PowerShell $ExactVersion. Expected CLR $($ExpectedProfile.dotnetMajor) but '$ResolvedExecutable' reported CLR $($Identity.dotNetMajor)." +} +if ($null -ne $ExpectedDotNetRuntimeVersion -and [version]$Identity.dotNetVersion -ne $ExpectedDotNetRuntimeVersion) { + throw "CLR runtime version mismatch for PowerShell $ExactVersion. Expected CLR $ExpectedDotNetRuntimeVersion but '$ResolvedExecutable' reported CLR $($Identity.dotNetVersion)." +} +if ($PSCmdlet.ParameterSetName -eq 'Provider') { + if ($Identity.platform -ne $Platform -or $Identity.architecture -ne $Architecture) { + throw "Runtime identity mismatch. Expected $Platform/$Architecture but '$ResolvedExecutable' reported $($Identity.platform)/$($Identity.architecture)." + } + if (-not (Test-PathWithinRoot -Path $Identity.psHome -Root $ExpectedPayloadRoot)) { + throw "Rejected PowerShell host whose PSHOME is outside the expected official payload root: $($Identity.psHome)" + } + if (-not (Test-PathWithinRoot -Path $Identity.processPath -Root $ExpectedPayloadRoot)) { + throw "Rejected PowerShell host shim whose process path is outside the expected official payload root: $($Identity.processPath)" + } +} + +$Result = [pscustomobject]@{ + Provider = $EffectiveProvider + PowerShellVersion = [string]$Identity.powerShellVersion + DotNetVersion = [string]$Identity.dotNetVersion + DotNetMajor = [int]$Identity.dotNetMajor + TargetFramework = [string]$ExpectedProfile.targetFramework + ExecutablePath = [string]$ResolvedExecutable + PSHome = [string]$Identity.psHome + ProcessPath = [string]$Identity.processPath + Platform = [string]$Identity.platform + Architecture = [string]$Identity.architecture +} + +if ($PassThru) { + $Result +} else { + $Result.ExecutablePath +} diff --git a/tools/Invoke-DLLPickleBuild.ps1 b/tools/Invoke-DLLPickleBuild.ps1 new file mode 100644 index 00000000..9f2bf27d --- /dev/null +++ b/tools/Invoke-DLLPickleBuild.ps1 @@ -0,0 +1,51 @@ +<# +.SYNOPSIS + Runs DLLPickle build tasks with the repository-pinned InvokeBuild version. + +.DESCRIPTION + Loads the exact InvokeBuild version declared in build/build-tool-versions.json + and invokes the requested tasks. Run this script from a fresh, non-profile + PowerShell process so previously loaded build-tool assemblies cannot affect it. + +.PARAMETER Task + One or more InvokeBuild task names. The default runs the build file's default task. + +.PARAMETER BuildFile + Path to the InvokeBuild build file. + +.PARAMETER ToolPolicyPath + Path to the exact build-tool version policy. + +.EXAMPLE + pwsh -NoProfile -NonInteractive -File ./tools/Invoke-DLLPickleBuild.ps1 -Task TestLocal + +.OUTPUTS + None. +#> + +[CmdletBinding()] +param( + [Parameter()] + [ValidateNotNullOrEmpty()] + [string[]]$Task = @('.'), + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$BuildFile = (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -ChildPath 'build/DLLPickle.Build.ps1'), + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$ToolPolicyPath = (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -ChildPath 'build/build-tool-versions.json') +) + +$ErrorActionPreference = 'Stop' +$RepositoryRoot = Split-Path -Path $PSScriptRoot -Parent +$ToolingScriptPath = Join-Path -Path $RepositoryRoot -ChildPath 'build/DLLPickle.Tooling.ps1' +. $ToolingScriptPath + +$ToolPolicy = Get-DLLPickleBuildToolPolicy -Path $ToolPolicyPath +$InvokeBuildVersion = Get-DLLPickleBuildToolVersion -Policy $ToolPolicy -Name 'InvokeBuild' +$null = Import-DLLPickleBuildTool -Name 'InvokeBuild' -RequiredVersion $InvokeBuildVersion + +$InvokeBuildCommand = Get-Command -Name 'Invoke-Build' -Module 'InvokeBuild' -ErrorAction Stop +& $InvokeBuildCommand -Task $Task -File $BuildFile diff --git a/tools/Invoke-DLLPickleManualAuthenticatedCompatibility.ps1 b/tools/Invoke-DLLPickleManualAuthenticatedCompatibility.ps1 new file mode 100644 index 00000000..45d60a38 --- /dev/null +++ b/tools/Invoke-DLLPickleManualAuthenticatedCompatibility.ps1 @@ -0,0 +1,297 @@ +<# +.SYNOPSIS +Collects resumable, sanitized interactive authentication evidence for the initial multi-target release. + +.DESCRIPTION +Runs fixed scenarios under each prepared exact Windows PowerShell executable. +Every scenario is a fresh process. Existing passing scenario checkpoints are +reused only when the source commit, bundle fingerprint, and complete prepared +module-inventory fingerprint match. The final candidate remains pending until a +maintainer reviews and explicitly accepts it. + +No token, tenant, account, mailbox, subscription, resource, or raw service +result is written to evidence. +#> + +[CmdletBinding()] +[OutputType([pscustomobject])] +param ( + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$WorkRoot = (Join-Path (Split-Path -Path $PSScriptRoot -Parent) 'artifacts/manual-authenticated'), + + [Parameter()] + [string[]]$ProfileKey, + + [Parameter()] + [string[]]$ScenarioId, + + [Parameter()] + [switch]$RerunCompleted, + + [Parameter()] + [string]$AzureSubscriptionId = $env:DLLPICKLE_MANUAL_AZURE_SUBSCRIPTION_ID, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$OutputPath +) + +$ErrorActionPreference = 'Stop' +$RepositoryRoot = Split-Path -Path $PSScriptRoot -Parent +. (Join-Path $PSScriptRoot 'DLLPickle.ManualAuthenticatedEvidence.ps1') +$ResolvedWorkRoot = [System.IO.Path]::GetFullPath($WorkRoot) +if ([string]::IsNullOrWhiteSpace($OutputPath)) { + $OutputPath = Join-Path $ResolvedWorkRoot 'manual-authenticated-evidence.candidate.json' +} +$PreparationSummaryPath = Join-Path $ResolvedWorkRoot 'preparation-summary.json' +$SessionPath = Join-Path $ResolvedWorkRoot 'capture-session.json' +$ScenarioRoot = Join-Path $ResolvedWorkRoot 'scenarios' +$MatrixPath = Join-Path $RepositoryRoot 'build/powershell-test-matrix.json' +$PolicyPath = Join-Path $RepositoryRoot 'build/dependency-policy.json' +$DLLPickleManifestPath = Join-Path $RepositoryRoot 'module/DLLPickle/DLLPickle.psd1' +$ChildHarnessPath = Join-Path $PSScriptRoot 'Invoke-DLLPickleManualAuthenticatedScenario.ps1' +$ValidatorPath = Join-Path $PSScriptRoot 'Test-DLLPickleManualAuthenticatedEvidence.ps1' + +foreach ($RequiredPath in @($PreparationSummaryPath, $MatrixPath, $PolicyPath, $DLLPickleManifestPath, $ChildHarnessPath, $ValidatorPath)) { + if (-not (Test-Path -LiteralPath $RequiredPath -PathType Leaf)) { + throw "Manual authenticated compatibility preparation is incomplete: $RequiredPath" + } +} +$Preparation = Get-Content -LiteralPath $PreparationSummaryPath -Raw | ConvertFrom-Json -ErrorAction Stop +$Matrix = Get-Content -LiteralPath $MatrixPath -Raw | ConvertFrom-Json -ErrorAction Stop +$AllProfileKeys = @( + $Matrix.profiles | ForEach-Object { + 'ps{0}.{1}-{2}-windows-x64' -f $_.powerShellMajor, $_.powerShellMinor, $_.targetFramework + } +) +$PreparedProfileKeys = @($Preparation.Profiles.ProfileKey) +if ($PreparedProfileKeys.Count -ne $AllProfileKeys.Count -or + @(Compare-Object -ReferenceObject @(Get-DLLPickleOrdinalSequence -InputObject $AllProfileKeys) -DifferenceObject @(Get-DLLPickleOrdinalSequence -InputObject $PreparedProfileKeys)).Count -gt 0) { + throw 'Preparation summary does not contain the exact required Windows runtime profile set.' +} +$Bundle = & (Join-Path $PSScriptRoot 'Get-DLLPickleBundleSourceFingerprint.ps1') -RepositoryRoot $RepositoryRoot +$SourceCommitSha = (git -C $RepositoryRoot rev-parse HEAD).Trim() +if ($LASTEXITCODE -ne 0 -or $SourceCommitSha -notmatch '^[a-f0-9]{40}$') { + throw 'Could not resolve the source commit for manual authenticated evidence.' +} +if ([string]$Preparation.BundleSourceFingerprint -ne [string]$Bundle.fingerprint) { + throw 'Prepared modules/runtimes belong to a different bundle fingerprint. Rerun Initialize-DLLPickleManualAuthenticatedCompatibility.ps1.' +} +$CurrentInventoryFingerprints = [ordered]@{} +foreach ($PreparedProfile in @($Preparation.Profiles)) { + if (-not (Test-Path -LiteralPath ([string]$PreparedProfile.InventoryPath) -PathType Leaf)) { + throw "Prepared inventory was not found for '$($PreparedProfile.ProfileKey)': $($PreparedProfile.InventoryPath)" + } + $PreparedInventory = Get-Content -LiteralPath ([string]$PreparedProfile.InventoryPath) -Raw | ConvertFrom-Json -ErrorAction Stop + $CurrentInventoryFingerprint = Get-DLLPicklePreparedInventoryFingerprint -Inventory $PreparedInventory + if ([string]$PreparedProfile.InventoryFingerprint -ne $CurrentInventoryFingerprint) { + throw "Prepared module inventory changed for '$($PreparedProfile.ProfileKey)'. Rerun Initialize-DLLPickleManualAuthenticatedCompatibility.ps1." + } + $CurrentInventoryFingerprints[[string]$PreparedProfile.ProfileKey] = $CurrentInventoryFingerprint +} + +if (Test-Path -LiteralPath $SessionPath -PathType Leaf) { + $Session = Get-Content -LiteralPath $SessionPath -Raw | ConvertFrom-Json -ErrorAction Stop + if ([string]$Session.sourceCommitSha -ne $SourceCommitSha -or [string]$Session.bundleSourceFingerprint -ne [string]$Bundle.fingerprint) { + throw "The existing capture session belongs to another commit or bundle. Preserve it and choose a new -WorkRoot." + } + foreach ($PreparedProfile in @($Preparation.Profiles)) { + $SessionFingerprintProperty = if ($null -ne $Session.inventoryFingerprints) { + $Session.inventoryFingerprints.PSObject.Properties[[string]$PreparedProfile.ProfileKey] + } else { + $null + } + $SessionFingerprint = if ($null -ne $SessionFingerprintProperty) { $SessionFingerprintProperty.Value } else { $null } + if ([string]$SessionFingerprint -ne [string]$CurrentInventoryFingerprints[[string]$PreparedProfile.ProfileKey]) { + throw "The existing capture session belongs to another prepared module inventory for '$($PreparedProfile.ProfileKey)'. Preserve it and choose a new -WorkRoot." + } + } +} else { + $Session = [pscustomobject][ordered]@{ + schemaVersion = 1 + sourceCommitSha = $SourceCommitSha + bundleSourceFingerprint = [string]$Bundle.fingerprint + inventoryFingerprints = $CurrentInventoryFingerprints + captureStartedAtUtc = [System.DateTimeOffset]::UtcNow.ToString('o') + credentialMode = 'delegated-interactive' + credentialMaterialCaptured = $false + writesPerformed = $false + } + $null = New-Item -Path $ResolvedWorkRoot -ItemType Directory -Force + $Session | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $SessionPath -Encoding utf8NoBOM +} +$CaptureStartedAtUtc = ConvertTo-DLLPickleUtcDateTimeOffset -Value $Session.captureStartedAtUtc +$ExpiresAtUtc = $CaptureStartedAtUtc.AddDays(14) +if ([System.DateTimeOffset]::UtcNow -ge $ExpiresAtUtc) { + throw "Manual authenticated evidence capture expired at $($ExpiresAtUtc.ToString('o')). Preserve it and start a new work root." +} + +$AllScenarioIds = @( + 'graph-module-only', 'graph-dllpickle-first', 'graph-module-first', + 'exo-module-only', 'exo-dllpickle-first', 'exo-module-first', + 'az-module-only', 'az-dllpickle-first', 'az-module-first', + 'teams-module-only', 'teams-dllpickle-first', 'teams-module-first', + 'cross-import-order-1', 'cross-import-order-2' +) +$SelectedProfileKeys = if ($ProfileKey.Count -gt 0) { @($ProfileKey) } else { $AllProfileKeys } +$SelectedScenarioIds = if ($ScenarioId.Count -gt 0) { @($ScenarioId) } else { $AllScenarioIds } +$UnknownProfiles = @($SelectedProfileKeys | Where-Object { $_ -notin $AllProfileKeys }) +$UnknownScenarios = @($SelectedScenarioIds | Where-Object { $_ -notin $AllScenarioIds }) +if ($UnknownProfiles.Count -gt 0 -or $UnknownScenarios.Count -gt 0) { + throw "Unsupported profile/scenario selection. Profiles: '$($UnknownProfiles -join ', ')'; scenarios: '$($UnknownScenarios -join ', ')'." +} + +foreach ($CurrentProfileKey in $SelectedProfileKeys) { + $PreparedProfiles = @($Preparation.Profiles | Where-Object ProfileKey -eq $CurrentProfileKey) + if ($PreparedProfiles.Count -ne 1) { throw "Preparation summary has no unique '$CurrentProfileKey' profile." } + $PreparedProfile = $PreparedProfiles[0] + foreach ($CurrentScenarioId in $SelectedScenarioIds) { + $ScenarioOutputPath = Join-Path $ScenarioRoot "$CurrentProfileKey/$CurrentScenarioId.json" + if (-not $RerunCompleted.IsPresent -and (Test-Path -LiteralPath $ScenarioOutputPath -PathType Leaf)) { + $ExistingScenario = Get-Content -LiteralPath $ScenarioOutputPath -Raw | ConvertFrom-Json -ErrorAction Stop + if ([string]$ExistingScenario.status -eq 'passed' -and + [string]$ExistingScenario.scenarioId -eq $CurrentScenarioId -and + [string]$ExistingScenario.profileKey -eq $CurrentProfileKey -and + [string]$ExistingScenario.powerShellVersion -eq [string]$PreparedProfile.PowerShellVersion -and + [string]$ExistingScenario.targetFramework -eq [string]$PreparedProfile.TargetFramework -and + [string]$ExistingScenario.platform -eq 'windows' -and + [string]$ExistingScenario.architecture -eq 'x64' -and + [string]$ExistingScenario.inventoryFingerprint -eq [string]$PreparedProfile.InventoryFingerprint) { + Write-Information -MessageData "Reusing passing checkpoint: $CurrentProfileKey / $CurrentScenarioId" -InformationAction Continue + continue + } + } + + Write-Information -MessageData '' -InformationAction Continue + Write-Information -MessageData "Interactive scenario: $CurrentProfileKey / $CurrentScenarioId" -InformationAction Continue + Write-Information -MessageData 'Complete only the provider sign-in prompts shown by the fixed child harness. No raw service output is retained.' -InformationAction Continue + $ChildArguments = @( + '-NoLogo', '-NoProfile', '-File', $ChildHarnessPath, + '-ScenarioId', $CurrentScenarioId, + '-InventoryPath', [string]$PreparedProfile.InventoryPath, + '-PolicyPath', $PolicyPath, + '-DLLPickleManifestPath', $DLLPickleManifestPath, + '-OutputPath', $ScenarioOutputPath, + '-ExpectedProfileKey', $CurrentProfileKey, + '-ExpectedPowerShellVersion', [string]$PreparedProfile.PowerShellVersion, + '-ExpectedTargetFramework', [string]$PreparedProfile.TargetFramework, + '-ExpectedInventoryFingerprint', [string]$PreparedProfile.InventoryFingerprint + ) + if (-not [string]::IsNullOrWhiteSpace($AzureSubscriptionId)) { + $ChildArguments += @('-AzureSubscriptionId', $AzureSubscriptionId) + } + & ([string]$PreparedProfile.ExecutablePath) @ChildArguments + if ($LASTEXITCODE -ne 0) { + throw "Interactive scenario '$CurrentProfileKey/$CurrentScenarioId' failed. Correct the sign-in or authorization problem, then rerun; passing checkpoints will be reused." + } + } +} + +$MissingScenarioPaths = @( + foreach ($ExpectedProfileKey in $AllProfileKeys) { + foreach ($ExpectedScenarioId in $AllScenarioIds) { + $ExpectedPath = Join-Path $ScenarioRoot "$ExpectedProfileKey/$ExpectedScenarioId.json" + if (-not (Test-Path -LiteralPath $ExpectedPath -PathType Leaf)) { $ExpectedPath } + } + } +) +if ($MissingScenarioPaths.Count -gt 0) { + return [pscustomobject]@{ + Complete = $false + MissingScenarioCount = $MissingScenarioPaths.Count + CandidateEvidencePath = $null + WritesPerformed = $false + } +} + +$Profiles = @( + foreach ($RuntimeProfile in @($Matrix.profiles)) { + $PowerShellLine = '{0}.{1}' -f $RuntimeProfile.powerShellMajor, $RuntimeProfile.powerShellMinor + $CurrentProfileKey = 'ps{0}-{1}-windows-x64' -f $PowerShellLine, $RuntimeProfile.targetFramework + $PreparedProfile = @($Preparation.Profiles | Where-Object ProfileKey -eq $CurrentProfileKey)[0] + $Inventory = Get-Content -LiteralPath ([string]$PreparedProfile.InventoryPath) -Raw | ConvertFrom-Json -ErrorAction Stop + $ScenarioRows = @( + foreach ($ExpectedScenarioId in $AllScenarioIds) { + $ScenarioPath = Join-Path $ScenarioRoot "$CurrentProfileKey/$ExpectedScenarioId.json" + Get-Content -LiteralPath $ScenarioPath -Raw | ConvertFrom-Json -ErrorAction Stop + } + ) + [ordered]@{ + profileKey = $CurrentProfileKey + powerShellVersion = [string]$RuntimeProfile.powerShellVersion + powerShellLine = $PowerShellLine + dotNetVersion = [string]$RuntimeProfile.dotnetRuntimeVersion + dotNetMajor = [int]$RuntimeProfile.dotnetMajor + targetFramework = [string]$RuntimeProfile.targetFramework + platform = 'windows' + architecture = 'x64' + runtimeExecutable = 'runtime:{0}' -f [System.IO.Path]::GetFileName([string]$PreparedProfile.ExecutablePath) + psHome = 'runtime:.' + writesPerformed = $false + inventoryFingerprint = [string]$PreparedProfile.InventoryFingerprint + moduleVersions = @( + foreach ($Module in @(Get-DLLPickleOrdinalSequence -InputObject @($Inventory.Modules) -KeySelector { param($Item) [string]$Item.Name } -Unique)) { + [ordered]@{ + name = [string]$Module.Name + version = [string]$Module.Version + manifest = ConvertTo-DLLPickleUpstreamManifestIdentifier -ManifestPath ([string]$Module.ModuleManifestPath) -ModuleCachePath ([string]$Inventory.ModuleCachePath) + } + } + ) + scenarios = $ScenarioRows + } + } +) +$CaptureCompletedAtUtc = [System.DateTimeOffset]::UtcNow +if ($CaptureCompletedAtUtc -ge $ExpiresAtUtc) { + throw "Manual authenticated evidence capture expired at $($ExpiresAtUtc.ToString('o')). Preserve it and start a new work root." +} +$Content = [ordered]@{ + bridge = [ordered]@{ + id = 'initial-powershell-7.4-7.6-multitargeting-major' + allowedReleaseVersion = '3.0.0' + expiresAtUtc = $ExpiresAtUtc.ToString('o') + } + bundleSourceFingerprint = [string]$Bundle.fingerprint + credentialMode = 'delegated-interactive' + credentialMaterialCaptured = $false + authorizationBoundaryValidated = $false + platformScope = 'windows-x64-only' + writesPerformed = $false + profiles = $Profiles +} +$Evidence = [ordered]@{ + schemaVersion = 1 + evidenceType = 'manual-interactive-transition' + contentFingerprint = $null + provenance = [ordered]@{ + sourceCommitSha = $SourceCommitSha + captureStartedAtUtc = [string]$Session.captureStartedAtUtc + captureCompletedAtUtc = $CaptureCompletedAtUtc.ToString('o') + } + acceptance = [ordered]@{ + status = 'pending' + acceptedAtUtc = $null + acceptedBy = $null + confidence = $null + } + content = $Content +} +$Evidence.contentFingerprint = Get-DLLPickleNormalizedEvidenceFingerprint -Evidence ([pscustomobject]$Evidence) +$OutputDirectory = Split-Path -Path $OutputPath -Parent +if ($OutputDirectory -and -not (Test-Path -LiteralPath $OutputDirectory -PathType Container)) { + $null = New-Item -Path $OutputDirectory -ItemType Directory -Force +} +$Evidence | ConvertTo-Json -Depth 100 | Set-Content -LiteralPath $OutputPath -Encoding utf8NoBOM +$Validation = & $ValidatorPath -EvidencePath $OutputPath -RepositoryRoot $RepositoryRoot -TestMatrixPath $MatrixPath -DependencyPolicyPath $PolicyPath -Mode Capture +[pscustomobject]@{ + Complete = $true + CandidateEvidencePath = [System.IO.Path]::GetFullPath($OutputPath) + EvidenceFingerprint = [string]$Validation.EvidenceFingerprint + BundleSourceFingerprint = [string]$Validation.BundleSourceFingerprint + AllowedReleaseVersion = [string]$Validation.AllowedReleaseVersion + ExpiresAtUtc = [string]$Validation.ExpiresAtUtc + AcceptanceStatus = 'pending' + WritesPerformed = $false +} diff --git a/tools/Invoke-DLLPickleManualAuthenticatedScenario.ps1 b/tools/Invoke-DLLPickleManualAuthenticatedScenario.ps1 new file mode 100644 index 00000000..4805d520 --- /dev/null +++ b/tools/Invoke-DLLPickleManualAuthenticatedScenario.ps1 @@ -0,0 +1,236 @@ +<# +.SYNOPSIS +Runs one fixed interactive authenticated compatibility scenario in the current process. + +.DESCRIPTION +This child harness is invoked by Invoke-DLLPickleManualAuthenticatedCompatibility.ps1 +under an exact stock PowerShell executable. Authentication commands and read probes +are hard-coded. It writes sanitized JSON only: no token, tenant, account, subscription, +mailbox, resource, or raw service result is retained. +#> + +[CmdletBinding()] +param ( + [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$ScenarioId, + [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$InventoryPath, + [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$PolicyPath, + [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$DLLPickleManifestPath, + [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$OutputPath, + [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$ExpectedProfileKey, + [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$ExpectedPowerShellVersion, + [Parameter(Mandatory)][ValidatePattern('^net\d+\.0$')][string]$ExpectedTargetFramework, + [Parameter(Mandatory)][ValidatePattern('^[a-f0-9]{64}$')][string]$ExpectedInventoryFingerprint, + [Parameter()][string]$AzureSubscriptionId = $env:DLLPICKLE_MANUAL_AZURE_SUBSCRIPTION_ID +) + +$ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'DLLPickle.ManualAuthenticatedEvidence.ps1') + +function Get-SanitizedAssemblySnapshot { + $Rows = @(& $SnapshotHelper -PolicyPath $ResolvedPolicyPath) + @( + foreach ($Row in $Rows) { + [ordered]@{ + name = [string]$Row.Name + version = [string]$Row.Version + sha256 = ([string]$Row.Sha256).ToLowerInvariant() + selectedAsset = ConvertTo-DLLPickleManualEvidencePath -Path ([string]$Row.Path) -ModuleCacheRoot $ModuleCacheRoot -DLLPickleRoot $DLLPickleRoot -RuntimeRoot $PSHOME + assemblyLoadContext = [string]$Row.Alc + isCollectible = [bool]$Row.IsCollectible + } + } + ) +} + +function Import-ExactModuleSet { + param([Parameter(Mandatory)][string[]]$Names) + + foreach ($Name in $Names) { + $Rows = @($Inventory.Modules | Where-Object Name -eq $Name) + if ($Rows.Count -ne 1) { throw "Inventory does not contain exactly one '$Name' module." } + Import-Module -Name ([string]$Rows[0].ModuleManifestPath) -Force -ErrorAction Stop + } +} + +function Import-DLLPickleBundle { + Import-Module -Name $ResolvedDLLPickleManifestPath -Force -ErrorAction Stop + $ImportResults = @(Import-DPLibrary -SuppressLogo -ErrorAction Stop) + $FailedImports = @($ImportResults | Where-Object { [string]$_.Status -eq 'Failed' }) + if ($FailedImports.Count -gt 0) { + throw "DLLPickle preload reported $($FailedImports.Count) failed assembly load(s)." + } +} + +function Connect-Provider { + param( + [Parameter(Mandatory)][ValidateSet('graph', 'exo', 'az', 'teams')][string]$Provider, + [Parameter()][string]$SubscriptionId + ) + + switch ($Provider) { + 'graph' { + $Command = Get-DLLPickleAuthenticatedCommand -Name 'Connect-MgGraph' -Module 'Microsoft.Graph.Authentication' + & $Command -Scopes 'User.Read' -ContextScope Process -NoWelcome -ErrorAction Stop | Out-Null + } + 'exo' { + $Command = Get-DLLPickleAuthenticatedCommand -Name 'Connect-ExchangeOnline' -Module 'ExchangeOnlineManagement' + & $Command -ShowBanner:$false -ErrorAction Stop | Out-Null + } + 'az' { + $Command = Get-DLLPickleAuthenticatedCommand -Name 'Connect-AzAccount' -Module 'Az.Accounts' + # WAM depends on the interactive host and can fail after account selection. + # Device code remains delegated-interactive and avoids changing persisted Az config. + & $Command -Scope Process -UseDeviceAuthentication -ErrorAction Stop | Out-Null + if (-not [string]::IsNullOrWhiteSpace($SubscriptionId)) { + $Command = Get-DLLPickleAuthenticatedCommand -Name 'Set-AzContext' -Module 'Az.Accounts' + & $Command -SubscriptionId $SubscriptionId -Scope Process -ErrorAction Stop | Out-Null + } + } + 'teams' { + $Command = Get-DLLPickleAuthenticatedCommand -Name 'Connect-MicrosoftTeams' -Module 'MicrosoftTeams' + & $Command -ErrorAction Stop | Out-Null + } + } +} + +function Disconnect-Provider { + param([Parameter(Mandatory)][ValidateSet('graph', 'exo', 'az', 'teams')][string]$Provider) + + try { + switch ($Provider) { + 'graph' { + $Command = Get-DLLPickleAuthenticatedCommand -Name 'Disconnect-MgGraph' -Module 'Microsoft.Graph.Authentication' + & $Command -ErrorAction SilentlyContinue | Out-Null + } + 'exo' { + $Command = Get-DLLPickleAuthenticatedCommand -Name 'Disconnect-ExchangeOnline' -Module 'ExchangeOnlineManagement' + & $Command -Confirm:$false -ErrorAction SilentlyContinue | Out-Null + } + 'az' { + $Command = Get-DLLPickleAuthenticatedCommand -Name 'Clear-AzContext' -Module 'Az.Accounts' + & $Command -Scope Process -Force -ErrorAction SilentlyContinue | Out-Null + } + 'teams' { + $Command = Get-DLLPickleAuthenticatedCommand -Name 'Disconnect-MicrosoftTeams' -Module 'MicrosoftTeams' + & $Command -ErrorAction SilentlyContinue | Out-Null + } + } + } catch { + # Cleanup failures must not replace the sanitized scenario result. + Write-Verbose "Provider cleanup for '$Provider' did not complete." + } +} + +foreach ($RequiredPath in @($InventoryPath, $PolicyPath, $DLLPickleManifestPath)) { + if (-not (Test-Path -LiteralPath $RequiredPath -PathType Leaf)) { throw "Required scenario input was not found: $RequiredPath" } +} +$ResolvedInventoryPath = (Resolve-Path -LiteralPath $InventoryPath).Path +$ResolvedPolicyPath = (Resolve-Path -LiteralPath $PolicyPath).Path +$ResolvedDLLPickleManifestPath = (Resolve-Path -LiteralPath $DLLPickleManifestPath).Path +$DLLPickleRoot = Split-Path -Path $ResolvedDLLPickleManifestPath -Parent +$SnapshotHelper = Join-Path $PSScriptRoot 'Get-DLLPickleLoadedTrackedAssembly.ps1' +$Inventory = Get-Content -LiteralPath $ResolvedInventoryPath -Raw | ConvertFrom-Json -ErrorAction Stop +$InventoryFingerprint = [string]$Inventory.InventoryFingerprint +if ($InventoryFingerprint -ne $ExpectedInventoryFingerprint) { + throw "Prepared module inventory fingerprint '$InventoryFingerprint' does not match expected '$ExpectedInventoryFingerprint'." +} +$ModuleCacheRoot = [string]$Inventory.ModuleCachePath +$ActualTargetFramework = 'net{0}.0' -f [Environment]::Version.Major +$ActualProfileKey = 'ps{0}.{1}-{2}-windows-x64' -f $PSVersionTable.PSVersion.Major, $PSVersionTable.PSVersion.Minor, $ActualTargetFramework +if ($ActualProfileKey -ne $ExpectedProfileKey -or + $PSVersionTable.PSVersion.ToString() -ne $ExpectedPowerShellVersion -or + $ActualTargetFramework -ne $ExpectedTargetFramework) { + throw "Scenario runtime mismatch: expected PowerShell $ExpectedPowerShellVersion/$ExpectedTargetFramework; observed $($PSVersionTable.PSVersion)/$ActualTargetFramework." +} +if ([string]$Inventory.Profile.Platform -ne 'windows' -or [string]$Inventory.Profile.Architecture -ne 'x64') { + throw 'Manual authenticated transition evidence is restricted to Windows x64.' +} +$env:PSModulePath = @($ModuleCacheRoot, (Join-Path $PSHOME 'Modules')) -join [System.IO.Path]::PathSeparator + +$Definitions = [ordered]@{ + 'graph-module-only' = [ordered]@{ providers = @('graph'); modules = @('Microsoft.Graph.Authentication'); timing = 'module-only'; probes = @('graph-context', 'graph-me-read') } + 'graph-dllpickle-first' = [ordered]@{ providers = @('graph'); modules = @('Microsoft.Graph.Authentication'); timing = 'dllpickle-first'; probes = @('graph-context', 'graph-me-read') } + 'graph-module-first' = [ordered]@{ providers = @('graph'); modules = @('Microsoft.Graph.Authentication'); timing = 'module-first'; probes = @('graph-context', 'graph-me-read') } + 'exo-module-only' = [ordered]@{ providers = @('exo'); modules = @('ExchangeOnlineManagement'); timing = 'module-only'; probes = @('exo-mailbox-read') } + 'exo-dllpickle-first' = [ordered]@{ providers = @('exo'); modules = @('ExchangeOnlineManagement'); timing = 'dllpickle-first'; probes = @('exo-mailbox-read') } + 'exo-module-first' = [ordered]@{ providers = @('exo'); modules = @('ExchangeOnlineManagement'); timing = 'module-first'; probes = @('exo-mailbox-read') } + 'az-module-only' = [ordered]@{ providers = @('az'); modules = @('Az.Accounts', 'Az.Resources', 'Az.Storage'); timing = 'module-only'; probes = @('az-context', 'az-resource-read', 'az-storage-account-read') } + 'az-dllpickle-first' = [ordered]@{ providers = @('az'); modules = @('Az.Accounts', 'Az.Resources', 'Az.Storage'); timing = 'dllpickle-first'; probes = @('az-context', 'az-resource-read', 'az-storage-account-read') } + 'az-module-first' = [ordered]@{ providers = @('az'); modules = @('Az.Accounts', 'Az.Resources', 'Az.Storage'); timing = 'module-first'; probes = @('az-context', 'az-resource-read', 'az-storage-account-read') } + 'teams-module-only' = [ordered]@{ providers = @('teams'); modules = @('MicrosoftTeams'); timing = 'module-only'; probes = @('teams-tenant-read') } + 'teams-dllpickle-first' = [ordered]@{ providers = @('teams'); modules = @('MicrosoftTeams'); timing = 'dllpickle-first'; probes = @('teams-tenant-read') } + 'teams-module-first' = [ordered]@{ providers = @('teams'); modules = @('MicrosoftTeams'); timing = 'module-first'; probes = @('teams-tenant-read') } +} +$Policy = Get-Content -LiteralPath $ResolvedPolicyPath -Raw | ConvertFrom-Json +$ProfilePolicy = @($Policy.runtimeProfiles | Where-Object { + $_.powerShellLine -eq [string]$Inventory.Profile.PowerShellLine -and $_.targetFramework -eq $ExpectedTargetFramework + }) +if ($ProfilePolicy.Count -ne 1) { throw 'No unique dependency policy matches the authenticated scenario runtime.' } +$Definitions['cross-import-order-1'] = [ordered]@{ providers = @('graph', 'exo', 'az', 'teams'); modules = @($ProfilePolicy[0].importOrders[0]); timing = 'dllpickle-first'; probes = @('graph-context', 'graph-me-read', 'exo-mailbox-read', 'az-context', 'teams-tenant-read') } +$Definitions['cross-import-order-2'] = [ordered]@{ providers = @('graph', 'exo', 'az', 'teams'); modules = @($ProfilePolicy[0].importOrders[1]); timing = 'dllpickle-first'; probes = @('graph-context', 'graph-me-read', 'exo-mailbox-read', 'az-context', 'teams-tenant-read') } +if (-not $Definitions.Contains($ScenarioId)) { throw "Unsupported authenticated scenario '$ScenarioId'." } +$Definition = $Definitions[$ScenarioId] + +$Result = [ordered]@{ + scenarioId = $ScenarioId + profileKey = $ActualProfileKey + powerShellVersion = $PSVersionTable.PSVersion.ToString() + targetFramework = $ActualTargetFramework + platform = 'windows' + architecture = 'x64' + inventoryFingerprint = $InventoryFingerprint + importOrder = @($Definition.modules) + dllPickleTiming = [string]$Definition.timing + expectedTokenAudiences = @( + Get-DLLPickleOrdinalSequence -InputObject @( + foreach ($Provider in @($Definition.providers)) { + switch ($Provider) { + 'graph' { 'https://graph.microsoft.com' } + 'exo' { 'https://outlook.office365.com' } + 'az' { 'https://management.azure.com' } + 'teams' { 'https://api.spaces.skype.com' } + } + } + ) -Unique + ) + status = 'failed' + writesPerformed = $false + probes = @() + snapshots = @() + errorType = $null +} +$ConnectedProviders = [System.Collections.Generic.List[string]]::new() +try { + if ($Definition.timing -eq 'dllpickle-first') { Import-DLLPickleBundle } + Import-ExactModuleSet -Names @($Definition.modules) + if ($Definition.timing -eq 'module-first') { Import-DLLPickleBundle } + $Result.snapshots += [ordered]@{ stage = 'before-authentication'; assemblies = @(Get-SanitizedAssemblySnapshot) } + foreach ($Provider in @($Definition.providers)) { + Connect-Provider -Provider $Provider -SubscriptionId $AzureSubscriptionId + $ConnectedProviders.Add($Provider) + } + $Result.snapshots += [ordered]@{ stage = 'after-connection'; assemblies = @(Get-SanitizedAssemblySnapshot) } + $Result.probes = @( + foreach ($ProbeId in @($Definition.probes)) { Invoke-DLLPickleAuthenticatedReadProbe -ProbeId $ProbeId } + ) + $Result.snapshots += [ordered]@{ stage = 'after-read-probe'; assemblies = @(Get-SanitizedAssemblySnapshot) } + if (@($Result.probes | Where-Object status -ne 'passed').Count -eq 0) { + $Result.status = 'passed' + } +} catch { + $Result.errorType = $_.Exception.GetType().FullName +} finally { + for ($ProviderIndex = $ConnectedProviders.Count - 1; $ProviderIndex -ge 0; $ProviderIndex--) { + Disconnect-Provider -Provider $ConnectedProviders[$ProviderIndex] + } +} + +$OutputDirectory = Split-Path -Path $OutputPath -Parent +if ($OutputDirectory -and -not (Test-Path -LiteralPath $OutputDirectory -PathType Container)) { + $null = New-Item -Path $OutputDirectory -ItemType Directory -Force +} +[pscustomobject]$Result | ConvertTo-Json -Depth 100 | Set-Content -LiteralPath $OutputPath -Encoding utf8NoBOM +if ($Result.status -ne 'passed') { + throw "Authenticated scenario '$ScenarioId' failed. Sanitized error type: '$($Result.errorType)'. See '$OutputPath'." +} +[pscustomobject]$Result diff --git a/tools/New-DLLPickleArtifactSizeReport.ps1 b/tools/New-DLLPickleArtifactSizeReport.ps1 new file mode 100644 index 00000000..3e67487a --- /dev/null +++ b/tools/New-DLLPickleArtifactSizeReport.ps1 @@ -0,0 +1,197 @@ +<# +.SYNOPSIS + Generates deterministic unpacked and compressed module-size evidence. + +.DESCRIPTION + Measures each shipped target-framework payload and the complete module. The + committed baseline supplies material-growth thresholds. Compressed sizes use a + sorted in-memory ZIP with fixed timestamps so reruns are stable. + +.PARAMETER Strict + Throw when a baseline entry is missing or a material unpacked-size increase occurs. +#> + +[CmdletBinding()] +param( + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$ModulePath = (Join-Path (Split-Path -Parent $PSScriptRoot) 'module/DLLPickle'), + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$SupportPolicyPath = (Join-Path (Split-Path -Parent $PSScriptRoot) 'src/DLLPickle/SupportedRuntimeProfiles.json'), + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$BaselinePath = (Join-Path (Split-Path -Parent $PSScriptRoot) 'build/artifact-size-baseline.json'), + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$OutputPath = (Join-Path (Split-Path -Parent $PSScriptRoot) 'artifacts/package/artifact-size.json'), + + [Parameter()] + [switch]$Strict +) + +$ErrorActionPreference = 'Stop' +Add-Type -AssemblyName System.IO.Compression + +function Get-DLLPickleDeterministicCompressedSize { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string]$RootPath + ) + + $MemoryStream = [System.IO.MemoryStream]::new() + try { + $Archive = [System.IO.Compression.ZipArchive]::new($MemoryStream, [System.IO.Compression.ZipArchiveMode]::Create, $true) + try { + $Files = @(Get-ChildItem -LiteralPath $RootPath -File -Recurse | Sort-Object FullName) + foreach ($File in $Files) { + $EntryName = [System.IO.Path]::GetRelativePath($RootPath, $File.FullName).Replace('\', '/') + $Entry = $Archive.CreateEntry($EntryName, [System.IO.Compression.CompressionLevel]::Optimal) + $Entry.LastWriteTime = [System.DateTimeOffset]::new(1980, 1, 1, 0, 0, 0, [System.TimeSpan]::Zero) + $InputStream = $File.OpenRead() + $OutputStream = $Entry.Open() + try { + $InputStream.CopyTo($OutputStream) + } finally { + $OutputStream.Dispose() + $InputStream.Dispose() + } + } + } finally { + $Archive.Dispose() + } + return $MemoryStream.Length + } finally { + $MemoryStream.Dispose() + } +} + +function Get-DLLPickleSizeMeasurement { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string]$Name, + + [Parameter(Mandatory)] + [string]$Path, + + [Parameter()] + [object]$Baseline, + + [Parameter(Mandatory)] + [double]$MaximumIncreasePercent, + + [Parameter(Mandatory)] + [long]$MaximumIncreaseBytes + ) + + $Files = @(Get-ChildItem -LiteralPath $Path -File -Recurse) + $UnpackedBytes = [long](($Files | Measure-Object Length -Sum).Sum) + $CompressedBytes = [long](Get-DLLPickleDeterministicCompressedSize -RootPath $Path) + $HasBaseline = $null -ne $Baseline + $BaselineUnpackedBytes = if ($HasBaseline) { [long]$Baseline.unpackedBytes } else { $null } + $BaselineCompressedBytes = if ($HasBaseline) { [long]$Baseline.compressedBytes } else { $null } + $UnpackedDeltaBytes = if ($HasBaseline) { $UnpackedBytes - $BaselineUnpackedBytes } else { $null } + $CompressedDeltaBytes = if ($HasBaseline) { $CompressedBytes - $BaselineCompressedBytes } else { $null } + $PercentThresholdBytes = if ($HasBaseline) { [long][Math]::Ceiling($BaselineUnpackedBytes * ($MaximumIncreasePercent / 100)) } else { $null } + $AllowedIncreaseBytes = if ($HasBaseline) { [Math]::Max($MaximumIncreaseBytes, $PercentThresholdBytes) } else { $null } + $ReviewRequired = -not $HasBaseline -or $UnpackedDeltaBytes -gt $AllowedIncreaseBytes + + [PSCustomObject]@{ + Name = $Name + FileCount = $Files.Count + UnpackedBytes = $UnpackedBytes + CompressedBytes = $CompressedBytes + BaselinePresent = $HasBaseline + BaselineUnpackedBytes = $BaselineUnpackedBytes + BaselineCompressedBytes = $BaselineCompressedBytes + UnpackedDeltaBytes = $UnpackedDeltaBytes + CompressedDeltaBytes = $CompressedDeltaBytes + AllowedUnpackedIncreaseBytes = $AllowedIncreaseBytes + ReviewRequired = $ReviewRequired + } +} + +foreach ($RequiredPath in @($ModulePath, $SupportPolicyPath, $BaselinePath)) { + if (-not (Test-Path -LiteralPath $RequiredPath)) { + throw "Required size-report path was not found: $RequiredPath" + } +} + +$ResolvedModulePath = (Resolve-Path -LiteralPath $ModulePath).Path +$SupportPolicy = Get-Content -LiteralPath $SupportPolicyPath -Raw | ConvertFrom-Json -ErrorAction Stop +$Baseline = Get-Content -LiteralPath $BaselinePath -Raw | ConvertFrom-Json -ErrorAction Stop +$BaselineApprovalStatus = [string]$Baseline.approvalStatus +$ApprovedAtUtc = [System.DateTimeOffset]::MinValue +$HasValidApprovalTimestamp = [System.DateTimeOffset]::TryParse( + [string]$Baseline.approvedAtUtc, + [System.Globalization.CultureInfo]::InvariantCulture, + [System.Globalization.DateTimeStyles]::AssumeUniversal, + [ref]$ApprovedAtUtc +) +$BaselineApproved = $BaselineApprovalStatus -ceq 'accepted' -and $HasValidApprovalTimestamp +$MaximumIncreasePercent = [double]$Baseline.thresholds.maximumIncreasePercent +$MaximumIncreaseBytes = [long]$Baseline.thresholds.maximumIncreaseBytes +$ExpectedTargetFrameworks = @($SupportPolicy.profiles.targetFramework | ForEach-Object { [string]$_ } | Sort-Object -Unique) + +$Measurements = @( + foreach ($TargetFramework in $ExpectedTargetFrameworks) { + $TfmPath = Join-Path (Join-Path $ResolvedModulePath 'bin') $TargetFramework + if (-not (Test-Path -LiteralPath $TfmPath -PathType Container)) { + [PSCustomObject]@{ + Name = $TargetFramework + FileCount = 0 + UnpackedBytes = 0 + CompressedBytes = 0 + BaselinePresent = $false + BaselineUnpackedBytes = 0 + BaselineCompressedBytes = 0 + UnpackedDeltaBytes = 0 + CompressedDeltaBytes = 0 + AllowedUnpackedIncreaseBytes = 0 + ReviewRequired = $true + Error = "Target-framework directory was not found: $TfmPath" + } + continue + } + $BaselineEntry = @($Baseline.profiles | Where-Object name -EQ $TargetFramework | Select-Object -First 1) + Get-DLLPickleSizeMeasurement -Name $TargetFramework -Path $TfmPath -Baseline $BaselineEntry[0] -MaximumIncreasePercent $MaximumIncreasePercent -MaximumIncreaseBytes $MaximumIncreaseBytes + } +) +$FullBaseline = @($Baseline.fullArtifact | Select-Object -First 1) +$FullMeasurement = Get-DLLPickleSizeMeasurement -Name 'fullArtifact' -Path $ResolvedModulePath -Baseline $FullBaseline[0] -MaximumIncreasePercent $MaximumIncreasePercent -MaximumIncreaseBytes $MaximumIncreaseBytes +$SizeGrowthReviewRequired = @($Measurements | Where-Object ReviewRequired).Count -gt 0 -or $FullMeasurement.ReviewRequired +$ReviewRequired = -not $BaselineApproved -or $SizeGrowthReviewRequired + +$Report = [PSCustomObject]@{ + SchemaVersion = 1 + GeneratedAtUtc = [System.DateTimeOffset]::UtcNow.ToString('o') + ModulePath = $ResolvedModulePath + BaselineApprovalStatus = $BaselineApprovalStatus + BaselineApproved = $BaselineApproved + BaselineApprovedAtUtc = if ($BaselineApproved) { $ApprovedAtUtc.ToUniversalTime().ToString('o') } else { $null } + Thresholds = $Baseline.thresholds + Profiles = @($Measurements) + FullArtifact = $FullMeasurement + SizeGrowthReviewRequired = $SizeGrowthReviewRequired + ReviewRequired = $ReviewRequired +} + +$OutputDirectory = Split-Path -Path $OutputPath -Parent +if ($OutputDirectory -and -not (Test-Path -LiteralPath $OutputDirectory -PathType Container)) { + $null = New-Item -Path $OutputDirectory -ItemType Directory -Force +} +$Report | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $OutputPath -Encoding UTF8 + +if ($Strict.IsPresent -and -not $BaselineApproved) { + throw "DLLPickle artifact size baseline is not accepted by a maintainer (status '$BaselineApprovalStatus' or approval timestamp missing). Review build/artifact-size-baseline.json." +} +if ($Strict.IsPresent -and $SizeGrowthReviewRequired) { + throw 'DLLPickle artifact size exceeds the approved material-growth policy or lacks a baseline. Review artifact-size.json.' +} + +$Report diff --git a/tools/New-DLLPickleConflictMatrix.ps1 b/tools/New-DLLPickleConflictMatrix.ps1 index 5c30760a..42d298d3 100644 --- a/tools/New-DLLPickleConflictMatrix.ps1 +++ b/tools/New-DLLPickleConflictMatrix.ps1 @@ -4,8 +4,9 @@ .DESCRIPTION Consumes the inventory object produced by Get-DLLPickleUpstreamInventory.ps1 (or its JSON, via -InventoryPath) and computes, per tracked assembly: which modules ship it, the distinct - versions, whether those versions diverge, and a placeholder AlcOwner field that the runtime - probe fills in later. The ConflictSurface is the set of assemblies that diverge across modules. + versions, selected hashes, and runtime ALC owners. The ConflictSurface is the set of assemblies + that diverge across modules. The profile fingerprint covers every selected tracked-assembly tuple + so a content or ALC move cannot pass merely because the assembly version stayed unchanged. .PARAMETER Inventory The inventory object (as returned by Get-DLLPickleUpstreamInventory.ps1). .PARAMETER InventoryPath @@ -33,6 +34,8 @@ $ErrorActionPreference = 'Stop' if ($PSCmdlet.ParameterSetName -eq 'Path') { $Inventory = Get-Content -LiteralPath $InventoryPath -Raw | ConvertFrom-Json } +$ProfileKey = if ($Inventory.PSObject.Properties.Name -contains 'ProfileKey') { [string]$Inventory.ProfileKey } else { $null } +$RequiresCompleteSelectionIdentity = -not [string]::IsNullOrWhiteSpace($ProfileKey) # Group every tracked assembly across all modules by assembly name. $ByAssembly = @{} @@ -41,9 +44,29 @@ foreach ($Module in $Inventory.Modules) { if (-not $ByAssembly.ContainsKey($Assembly.Name)) { $ByAssembly[$Assembly.Name] = [System.Collections.Generic.List[object]]::new() } + $Sha256 = if ($Assembly.PSObject.Properties.Name -contains 'Sha256') { + ([string]$Assembly.Sha256).ToLowerInvariant() + } else { + $null + } + $AlcOwner = if ($Assembly.PSObject.Properties.Name -contains 'Alc') { + [string]$Assembly.Alc + } elseif ($Assembly.PSObject.Properties.Name -contains 'AlcOwner') { + [string]$Assembly.AlcOwner + } else { + $null + } + if ($RequiresCompleteSelectionIdentity -and $Sha256 -notmatch '^[a-f0-9]{64}$') { + throw "Profile-keyed inventory '$ProfileKey' selection '$($Module.Name)/$($Assembly.Name)' requires a 64-character SHA-256." + } + if ($RequiresCompleteSelectionIdentity -and [string]::IsNullOrWhiteSpace($AlcOwner)) { + throw "Profile-keyed inventory '$ProfileKey' selection '$($Module.Name)/$($Assembly.Name)' requires an ALC owner." + } $ByAssembly[$Assembly.Name].Add([PSCustomObject]@{ - Module = $Module.Name - Version = [string]$Assembly.Version + Module = [string]$Module.Name + Version = [string]$Assembly.Version + Sha256 = $Sha256 + AlcOwner = $AlcOwner }) } } @@ -52,38 +75,63 @@ $AssemblyRows = foreach ($Name in ($ByAssembly.Keys | Sort-Object)) { $Entries = $ByAssembly[$Name] $DistinctVersions = @($Entries.Version | Sort-Object -Unique) $DistinctModules = @($Entries.Module | Sort-Object -Unique) + $DistinctHashes = @($Entries.Sha256 | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Sort-Object -Unique) + $DistinctAlcOwners = @($Entries.AlcOwner | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Sort-Object -Unique) + $Selections = @( + $Entries | + Sort-Object Module, Version, Sha256, AlcOwner | + ForEach-Object { + [PSCustomObject]@{ + Module = [string]$_.Module + Version = [string]$_.Version + Sha256 = [string]$_.Sha256 + AlcOwner = [string]$_.AlcOwner + } + } + ) [PSCustomObject]@{ - Name = $Name - ShippedBy = $DistinctModules - Versions = $DistinctVersions + Name = $Name + ShippedBy = $DistinctModules + Versions = $DistinctVersions + Hashes = $DistinctHashes + AlcOwners = $DistinctAlcOwners + Selections = $Selections # Diverges only when >=2 DISTINCT modules ship >=2 distinct versions. Counting distinct # modules (not raw entries) avoids a false positive when one module ships the same # assembly more than once (e.g. nested folders / multiple RIDs) at differing versions. - Diverges = ($DistinctModules.Count -ge 2 -and $DistinctVersions.Count -ge 2) - AlcOwner = $null # filled by the runtime probe / adjudication + Diverges = ($DistinctModules.Count -ge 2 -and $DistinctVersions.Count -ge 2) + # Retain the legacy scalar for older comparison consumers when ownership is unambiguous. + AlcOwner = if ($DistinctAlcOwners.Count -eq 1) { $DistinctAlcOwners[0] } else { $null } } } -# Versions- and contributor-aware fingerprint over the conflict surface. Each diverging assembly -# contributes its name, its sorted distinct versions, AND the sorted set of modules that ship it -# (ShippedBy). Including versions catches a material change where the same assemblies stay in conflict -# but their versions move (e.g. an upstream module bumps within-major). Including ShippedBy catches a -# change in WHICH modules contribute to a conflict even when the version set is unchanged (e.g. one -# module leaves a conflict as another joins at the same versions) -- the preload/block adjudication is -# module-specific, so that contributor change also needs human review. -# This is the single source of the drift fingerprint consumed by the Upstream-Compatibility workflow -# and the recorded baseline in build/dependency-policy.json. (ALC ownership is not included: it is -# null in the static inventory and is only known from the runtime probe / maintainer adjudication.) -$SurfaceRows = @( - $AssemblyRows | Where-Object Diverges | Sort-Object Name | ForEach-Object { - '{0}={1};by={2}' -f $_.Name, (@($_.Versions | Sort-Object) -join ','), (@($_.ShippedBy | Sort-Object) -join ',') +# Profile-aware evidence fingerprint over every selected tracked assembly. Per-selection tuples keep +# module, version, content hash, and runtime ALC associated instead of hashing independent sets that +# could collide when two modules swap payloads or load contexts. Absolute selected-asset paths are not +# canonical input because runner roots differ; the selected file's SHA-256 is the stable content identity. +$EvidenceRows = @( + $AssemblyRows | Sort-Object Name | ForEach-Object { + $CanonicalSelections = @( + $_.Selections | ForEach-Object { + 'module={0};version={1};sha256={2};alc={3}' -f $_.Module, $_.Version, $_.Sha256, $_.AlcOwner + } | Sort-Object + ) + '{0}|diverges={1}|{2}' -f $_.Name, ([string]$_.Diverges).ToLowerInvariant(), ($CanonicalSelections -join '|') } ) -$FingerprintBytes = [System.Text.Encoding]::UTF8.GetBytes(($SurfaceRows -join '|')) +$FingerprintInput = if ([string]::IsNullOrWhiteSpace($ProfileKey)) { + $EvidenceRows -join '|' +} else { + '{0}|{1}' -f $ProfileKey, ($EvidenceRows -join '|') +} +$FingerprintBytes = [System.Text.Encoding]::UTF8.GetBytes($FingerprintInput) $Fingerprint = [System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::HashData($FingerprintBytes)).Replace('-', '').ToLowerInvariant() $Matrix = [PSCustomObject]@{ GeneratedAtUtc = $null # stamped by the caller; avoids non-deterministic test output + ProfileKey = $ProfileKey + Profile = if ($Inventory.PSObject.Properties.Name -contains 'Profile') { $Inventory.Profile } else { $null } + ValidationTier = if ($Inventory.PSObject.Properties.Name -contains 'ValidationTier') { $Inventory.ValidationTier } else { $null } Assemblies = @($AssemblyRows) ConflictSurface = @($AssemblyRows | Where-Object Diverges | ForEach-Object Name) Fingerprint = $Fingerprint diff --git a/tools/New-DLLPickleDependencyChangeReport.ps1 b/tools/New-DLLPickleDependencyChangeReport.ps1 new file mode 100644 index 00000000..ae639d71 --- /dev/null +++ b/tools/New-DLLPickleDependencyChangeReport.ps1 @@ -0,0 +1,308 @@ +<# +.SYNOPSIS + Builds a per-TFM dependency-change report for a Dependabot candidate. + +.DESCRIPTION + Compares baseline and candidate NuGet target graphs, selected assets, and + packaged assembly inputs for every supported TFM. It also summarizes exact-host + Pester XML evidence and incorporates the package-size report. The report computes + a deterministic per-TFM delta for every assembly classified as preload or blocked; + live upstream and ALC adjudication remains owned by the profile-aware upstream gate. +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$BaselineProjectAssetsPath, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$CandidateProjectAssetsPath, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$BaselineBuildOutputRoot, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$CandidateBuildOutputRoot, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$SupportPolicyPath, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$DependencyPolicyPath, + + [Parameter()] + [string]$SizeReportPath, + + [Parameter()] + [string]$ScenarioEvidencePath, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$OutputPath = (Join-Path (Split-Path -Parent $PSScriptRoot) 'artifacts/dependency/dependency-change-report.json') +) + +$ErrorActionPreference = 'Stop' + +function Get-DLLPickleNuGetTargetGraph { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [object]$Assets, + + [Parameter(Mandatory)] + [string]$TargetFramework + ) + + if (-not $Assets -or $Assets.PSObject.Properties.Name -notcontains 'targets' -or -not $Assets.targets) { + throw 'NuGet project.assets.json contains no targets section; restore may be incomplete.' + } + + $TargetProperty = $Assets.targets.PSObject.Properties[$TargetFramework] + if (-not $TargetProperty) { + return @() + } + + @( + foreach ($LibraryProperty in @($TargetProperty.Value.PSObject.Properties | Sort-Object Name)) { + $SeparatorIndex = $LibraryProperty.Name.LastIndexOf('/') + $PackageName = if ($SeparatorIndex -gt 0) { $LibraryProperty.Name.Substring(0, $SeparatorIndex) } else { $LibraryProperty.Name } + $PackageVersion = if ($SeparatorIndex -gt 0) { $LibraryProperty.Name.Substring($SeparatorIndex + 1) } else { $null } + $CompileAssets = if ($LibraryProperty.Value.PSObject.Properties.Name -contains 'compile' -and $LibraryProperty.Value.compile) { + @($LibraryProperty.Value.compile.PSObject.Properties.Name | Where-Object { $_ -ne '_._' } | Sort-Object -Unique) + } else { + @() + } + $RuntimeAssets = if ($LibraryProperty.Value.PSObject.Properties.Name -contains 'runtime' -and $LibraryProperty.Value.runtime) { + @($LibraryProperty.Value.runtime.PSObject.Properties.Name | Where-Object { $_ -ne '_._' } | Sort-Object -Unique) + } else { + @() + } + [PSCustomObject]@{ + PackageName = $PackageName + PackageVersion = $PackageVersion + CompileAssets = @($CompileAssets) + RuntimeAssets = @($RuntimeAssets) + } + } + ) +} + +function Get-DLLPicklePackagedAssemblyInput { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string]$BuildOutputRoot, + + [Parameter(Mandatory)] + [string]$TargetFramework + ) + + $TfmPath = Join-Path $BuildOutputRoot $TargetFramework + if (-not (Test-Path -LiteralPath $TfmPath -PathType Container)) { + return @() + } + + $Files = @( + Get-ChildItem -LiteralPath $TfmPath -File -Filter '*.dll' | + Where-Object Name -Match '^(Azure\.|Microsoft\.|System\.)' + $RuntimePath = Join-Path $TfmPath 'runtimes' + if (Test-Path -LiteralPath $RuntimePath -PathType Container) { + Get-ChildItem -LiteralPath $RuntimePath -File -Recurse | + Where-Object FullName -Match '[\\/]native[\\/]' + } + ) + + @( + foreach ($File in @($Files | Sort-Object FullName)) { + $AssemblyName = $null + $AssemblyVersion = $null + try { + $ManagedIdentity = [System.Reflection.AssemblyName]::GetAssemblyName($File.FullName) + $AssemblyName = [string]$ManagedIdentity.Name + $AssemblyVersion = [string]$ManagedIdentity.Version + } catch { + # Native runtime payloads are still package inputs but do not have a managed identity. + $AssemblyName = $null + $AssemblyVersion = $null + } + $Sha256 = (Get-FileHash -LiteralPath $File.FullName -Algorithm SHA256).Hash.ToLowerInvariant() + [PSCustomObject]@{ + RelativePath = [System.IO.Path]::GetRelativePath($TfmPath, $File.FullName).Replace('\', '/') + Length = [long]$File.Length + Sha256 = $Sha256 + AssemblyName = $AssemblyName + AssemblyVersion = $AssemblyVersion + IdentityFingerprint = if ($AssemblyName) { "$AssemblyVersion|$Sha256" } else { $null } + } + } + ) +} + +function Compare-DLLPickleNamedRow { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [AllowEmptyCollection()] + [object[]]$Baseline, + + [Parameter(Mandatory)] + [AllowEmptyCollection()] + [object[]]$Candidate, + + [Parameter(Mandatory)] + [string]$KeyProperty, + + [Parameter(Mandatory)] + [string]$ValueProperty + ) + + $BaselineByKey = @{} + foreach ($Row in $Baseline) { + $Key = [string]$Row.$KeyProperty + if ($BaselineByKey.ContainsKey($Key)) { + throw "Baseline rows contain duplicate '$KeyProperty' value '$Key'." + } + $BaselineByKey[$Key] = $Row + } + $CandidateByKey = @{} + foreach ($Row in $Candidate) { + $Key = [string]$Row.$KeyProperty + if ($CandidateByKey.ContainsKey($Key)) { + throw "Candidate rows contain duplicate '$KeyProperty' value '$Key'." + } + $CandidateByKey[$Key] = $Row + } + $Keys = @($BaselineByKey.Keys + $CandidateByKey.Keys | Sort-Object -Unique) + + [PSCustomObject]@{ + Added = @($Keys | Where-Object { -not $BaselineByKey.ContainsKey($_) } | ForEach-Object { $CandidateByKey[$_] }) + Removed = @($Keys | Where-Object { -not $CandidateByKey.ContainsKey($_) } | ForEach-Object { $BaselineByKey[$_] }) + Changed = @( + foreach ($Key in $Keys) { + if ($BaselineByKey.ContainsKey($Key) -and $CandidateByKey.ContainsKey($Key) -and [string]$BaselineByKey[$Key].$ValueProperty -cne [string]$CandidateByKey[$Key].$ValueProperty) { + [PSCustomObject]@{ + Key = $Key + Baseline = $BaselineByKey[$Key].$ValueProperty + Candidate = $CandidateByKey[$Key].$ValueProperty + } + } + } + ) + } +} + +foreach ($RequiredPath in @($BaselineProjectAssetsPath, $CandidateProjectAssetsPath, $BaselineBuildOutputRoot, $CandidateBuildOutputRoot, $SupportPolicyPath, $DependencyPolicyPath)) { + if (-not (Test-Path -LiteralPath $RequiredPath)) { + throw "Required dependency-report path was not found: $RequiredPath" + } +} + +$BaselineAssets = Get-Content -LiteralPath $BaselineProjectAssetsPath -Raw | ConvertFrom-Json -ErrorAction Stop +$CandidateAssets = Get-Content -LiteralPath $CandidateProjectAssetsPath -Raw | ConvertFrom-Json -ErrorAction Stop +foreach ($AssetsInput in @( + [PSCustomObject]@{ Name = 'Baseline'; Path = $BaselineProjectAssetsPath; Value = $BaselineAssets } + [PSCustomObject]@{ Name = 'Candidate'; Path = $CandidateProjectAssetsPath; Value = $CandidateAssets } + )) { + if ($AssetsInput.Value.PSObject.Properties.Name -notcontains 'targets' -or -not $AssetsInput.Value.targets) { + throw "$($AssetsInput.Name) NuGet assets file has no targets section: $($AssetsInput.Path)" + } +} +$SupportPolicy = Get-Content -LiteralPath $SupportPolicyPath -Raw | ConvertFrom-Json -ErrorAction Stop +$DependencyPolicy = Get-Content -LiteralPath $DependencyPolicyPath -Raw | ConvertFrom-Json -ErrorAction Stop +$TargetFrameworks = @($SupportPolicy.profiles.targetFramework | ForEach-Object { [string]$_ } | Sort-Object -Unique) +$ConflictSensitiveAssemblyNames = @( + @($DependencyPolicy.preload.assemblyName) + @($DependencyPolicy.blockedPreloadAssemblies.assemblyName) +) | + ForEach-Object { [string]$_ } | + Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | + Sort-Object -Unique +$SizeReport = if ($SizeReportPath -and (Test-Path -LiteralPath $SizeReportPath -PathType Leaf)) { + Get-Content -LiteralPath $SizeReportPath -Raw | ConvertFrom-Json -ErrorAction Stop +} else { + $null +} + +$ProfileReports = @( + foreach ($TargetFramework in $TargetFrameworks) { + $BaselineGraph = @(Get-DLLPickleNuGetTargetGraph -Assets $BaselineAssets -TargetFramework $TargetFramework) + $CandidateGraph = @(Get-DLLPickleNuGetTargetGraph -Assets $CandidateAssets -TargetFramework $TargetFramework) + $BaselineInputs = @(Get-DLLPicklePackagedAssemblyInput -BuildOutputRoot $BaselineBuildOutputRoot -TargetFramework $TargetFramework) + $CandidateInputs = @(Get-DLLPicklePackagedAssemblyInput -BuildOutputRoot $CandidateBuildOutputRoot -TargetFramework $TargetFramework) + $BaselineConflictInputs = @($BaselineInputs | Where-Object AssemblyName -IN $ConflictSensitiveAssemblyNames) + $CandidateConflictInputs = @($CandidateInputs | Where-Object AssemblyName -IN $ConflictSensitiveAssemblyNames) + $ConflictDelta = Compare-DLLPickleNamedRow -Baseline $BaselineConflictInputs -Candidate $CandidateConflictInputs -KeyProperty RelativePath -ValueProperty IdentityFingerprint + $SizeRow = if ($SizeReport) { @($SizeReport.Profiles | Where-Object Name -EQ $TargetFramework | Select-Object -First 1)[0] } else { $null } + + [PSCustomObject]@{ + TargetFramework = $TargetFramework + BaselineResolvedGraph = @($BaselineGraph) + CandidateResolvedGraph = @($CandidateGraph) + ResolvedGraphDelta = Compare-DLLPickleNamedRow -Baseline $BaselineGraph -Candidate $CandidateGraph -KeyProperty PackageName -ValueProperty PackageVersion + CandidateSelectedAssets = @($CandidateGraph | Select-Object PackageName,PackageVersion,CompileAssets,RuntimeAssets) + AssemblyDelta = Compare-DLLPickleNamedRow -Baseline $BaselineInputs -Candidate $CandidateInputs -KeyProperty RelativePath -ValueProperty Sha256 + Size = $SizeRow + ConflictSurfaceDelta = [PSCustomObject]@{ + RequiredCheck = 'Validate upstream compatibility tooling' + SensitiveAssemblyNames = @($ConflictSensitiveAssemblyNames) + BaselineAssemblies = @($BaselineConflictInputs) + CandidateAssemblies = @($CandidateConflictInputs) + Delta = $ConflictDelta + HasChanges = ( + @($ConflictDelta.Added).Count -gt 0 -or + @($ConflictDelta.Removed).Count -gt 0 -or + @($ConflictDelta.Changed).Count -gt 0 + ) + UpstreamAlcAdjudication = 'required-profile-aware-gate' + } + } + } +) + +$ScenarioResults = @( + if ($ScenarioEvidencePath -and (Test-Path -LiteralPath $ScenarioEvidencePath -PathType Container)) { + foreach ($XmlFile in @(Get-ChildItem -LiteralPath $ScenarioEvidencePath -File -Filter '*.xml' -Recurse | Sort-Object FullName)) { + try { + [xml]$Xml = Get-Content -LiteralPath $XmlFile.FullName -Raw + $Root = $Xml.DocumentElement + [PSCustomObject]@{ + File = [System.IO.Path]::GetRelativePath($ScenarioEvidencePath, $XmlFile.FullName).Replace('\', '/') + Total = [int]$Root.total + Failures = [int]$Root.failures + Errors = [int]$Root.errors + NotRun = [int]$Root.'not-run' + Passed = ([int]$Root.failures + [int]$Root.errors) -eq 0 + } + } catch { + [PSCustomObject]@{ + File = [System.IO.Path]::GetRelativePath($ScenarioEvidencePath, $XmlFile.FullName).Replace('\', '/') + Passed = $false + Error = $_.Exception.Message + } + } + } + } +) + +$Report = [PSCustomObject]@{ + SchemaVersion = 2 + GeneratedAtUtc = [System.DateTimeOffset]::UtcNow.ToString('o') + TargetFrameworks = @($TargetFrameworks) + Profiles = @($ProfileReports) + ScenarioOutcomes = @($ScenarioResults) + RequiredChecks = @('Build gate', 'Validate upstream compatibility tooling', 'dependency-review') + ReviewRequired = $true +} + +$OutputDirectory = Split-Path -Path $OutputPath -Parent +if ($OutputDirectory -and -not (Test-Path -LiteralPath $OutputDirectory -PathType Container)) { + $null = New-Item -Path $OutputDirectory -ItemType Directory -Force +} +$Report | ConvertTo-Json -Depth 50 | Set-Content -LiteralPath $OutputPath -Encoding UTF8 +$Report diff --git a/tools/New-DLLPickleNormalizedProfileEvidence.ps1 b/tools/New-DLLPickleNormalizedProfileEvidence.ps1 new file mode 100644 index 00000000..44415200 --- /dev/null +++ b/tools/New-DLLPickleNormalizedProfileEvidence.ps1 @@ -0,0 +1,370 @@ +<# +.SYNOPSIS +Creates a durable, path-normalized upstream compatibility snapshot. + +.DESCRIPTION +Combines the exact-profile inventory, conflict matrix, deterministic scenario +evidence, and validation-gap report into a stable document suitable for source +control. Volatile run provenance is kept outside the fingerprinted content. +Runner-specific absolute paths are converted to upstream: or dllpickle: +identifiers so equivalent evidence recomputes to the same content fingerprint. + +.PARAMETER InventoryPath +Path to upstream-inventory.json. + +.PARAMETER ConflictMatrixPath +Path to conflict-matrix.json. + +.PARAMETER ScenarioEvidencePath +Path to scenario-evidence.json. + +.PARAMETER ValidationGapsPath +Path to validation-gaps.json. + +.PARAMETER OutputPath +Path to write the normalized evidence JSON. + +.PARAMETER SourceRunId +Optional CI run identifier retained as non-fingerprinted provenance. + +.PARAMETER SourceRunUrl +Optional CI run URL retained as non-fingerprinted provenance. + +.PARAMETER SourceCommitSha +Optional source commit retained as non-fingerprinted provenance. + +.PARAMETER CapturedAtUtc +Optional ISO-8601 capture time. Defaults to the inventory timestamp, then UTC now. + +.OUTPUTS +System.Management.Automation.PSCustomObject +#> + +[CmdletBinding()] +[OutputType([pscustomobject])] +param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$InventoryPath, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$ConflictMatrixPath, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$ScenarioEvidencePath, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$ValidationGapsPath, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$OutputPath, + + [Parameter()] + [string]$SourceRunId = $env:GITHUB_RUN_ID, + + [Parameter()] + [string]$SourceRunUrl, + + [Parameter()] + [string]$SourceCommitSha = $env:GITHUB_SHA, + + [Parameter()] + [string]$CapturedAtUtc +) + +$ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'DLLPickle.ProfileEvidence.ps1') + +function ConvertTo-CollapsedRelativePath { + param([Parameter(Mandatory)][string]$Path) + + $Segments = [System.Collections.Generic.List[string]]::new() + foreach ($Segment in @($Path -split '/')) { + if ([string]::IsNullOrWhiteSpace($Segment) -or $Segment -eq '.') { + continue + } + if ($Segment -eq '..') { + if ($Segments.Count -eq 0) { + throw "Evidence path '$Path' escapes its normalized root." + } + $Segments.RemoveAt($Segments.Count - 1) + continue + } + $Segments.Add($Segment) + } + $Segments -join '/' +} + +function ConvertTo-NormalizedEvidencePath { + param( + [Parameter(Mandatory)][string]$Path, + [Parameter(Mandatory)][string]$ModuleCachePath, + [Parameter(Mandatory)][string]$RuntimeRoot + ) + + if ([string]::IsNullOrWhiteSpace($Path)) { + throw 'An evidence asset path is empty.' + } + + $NormalizedPath = $Path.Replace('\', '/').TrimEnd('/') + $NormalizedCache = $ModuleCachePath.Replace('\', '/').TrimEnd('/') + $NormalizedRuntimeRoot = $RuntimeRoot.Replace('\', '/').TrimEnd('/') + $RelativePath = $null + $Prefix = $null + if ($NormalizedPath.StartsWith("$NormalizedCache/", [System.StringComparison]::OrdinalIgnoreCase)) { + $RelativePath = $NormalizedPath.Substring($NormalizedCache.Length + 1) + $Prefix = 'upstream' + } elseif ($NormalizedPath -match '(?i)/dllpickle-upstream-modules/[^/]+/(?.+)$') { + $RelativePath = $Matches.relative + $Prefix = 'upstream' + } elseif ($NormalizedPath -match '(?i)/module/DLLPickle/(?.+)$') { + $RelativePath = $Matches.relative + $Prefix = 'dllpickle' + } elseif ($NormalizedPath.StartsWith("$NormalizedRuntimeRoot/", [System.StringComparison]::OrdinalIgnoreCase)) { + $RelativePath = $NormalizedPath.Substring($NormalizedRuntimeRoot.Length + 1) + $Prefix = 'runtime' + } else { + throw "Evidence path '$Path' is outside the upstream module cache, DLLPickle module root, and exact runtime root." + } + + '{0}:{1}' -f $Prefix, (ConvertTo-CollapsedRelativePath -Path $RelativePath) +} + +foreach ($RequiredPath in @($InventoryPath, $ConflictMatrixPath, $ScenarioEvidencePath, $ValidationGapsPath)) { + if (-not (Test-Path -LiteralPath $RequiredPath -PathType Leaf)) { + throw "Required profile evidence input was not found: $RequiredPath" + } +} + +$Inventory = Get-Content -LiteralPath $InventoryPath -Raw | ConvertFrom-Json -ErrorAction Stop +$Matrix = Get-Content -LiteralPath $ConflictMatrixPath -Raw | ConvertFrom-Json -ErrorAction Stop +$ScenarioEvidence = Get-Content -LiteralPath $ScenarioEvidencePath -Raw | ConvertFrom-Json -ErrorAction Stop +$ValidationGaps = Get-Content -LiteralPath $ValidationGapsPath -Raw | ConvertFrom-Json -ErrorAction Stop + +$ProfileKey = [string]$Inventory.ProfileKey +if ([string]::IsNullOrWhiteSpace($ProfileKey) -or -not $Inventory.Profile) { + throw 'The upstream inventory is not keyed to an exact runtime profile.' +} +foreach ($Source in @($Matrix, $ScenarioEvidence)) { + if ([string]$Source.ProfileKey -ne $ProfileKey) { + throw "Profile evidence '$($Source.ProfileKey)' does not match inventory profile '$ProfileKey'." + } +} +$DerivedProfileKey = 'ps{0}-{1}-{2}-{3}' -f @( + [string]$Inventory.Profile.PowerShellLine + [string]$Inventory.Profile.TargetFramework + [string]$Inventory.Profile.Platform + [string]$Inventory.Profile.Architecture +) +if ($ProfileKey -ne $DerivedProfileKey) { + throw "Inventory profile key '$ProfileKey' does not match derived profile key '$DerivedProfileKey'." +} +if (-not $ScenarioEvidence.Passed -or $ScenarioEvidence.WritesPerformed -or + [string]$ScenarioEvidence.ValidationTier -ne 'deterministic-import-no-auth') { + throw "Scenario evidence for '$ProfileKey' is not a passing zero-write deterministic tier." +} +if ($ValidationGaps.writesPerformed -or + [string]$ValidationGaps.authenticatedReadOnly -ne 'not-run-no-approved-credentials') { + throw "Validation-gap evidence for '$ProfileKey' must record an unexecuted, zero-write authenticated tier." +} +if (@($Inventory.Modules).Count -eq 0 -or @($Matrix.Assemblies).Count -eq 0 -or @($ScenarioEvidence.Scenarios).Count -eq 0) { + throw "Profile evidence for '$ProfileKey' is incomplete." +} + +$ModuleCachePath = [string]$Inventory.ModuleCachePath +if ([string]::IsNullOrWhiteSpace($ModuleCachePath)) { + throw "Inventory for '$ProfileKey' has no module cache root for path normalization." +} +$RuntimeRoot = [string]$Inventory.Profile.PSHome +if ([string]::IsNullOrWhiteSpace($RuntimeRoot)) { + throw "Inventory for '$ProfileKey' has no exact runtime root for path normalization." +} + +$NormalizedModules = @( + foreach ($Module in @(Get-DLLPickleOrdinalSequence -InputObject @($Inventory.Modules) -KeySelector { param($Item) [string]$Item.Name })) { + $UnsortedSelectedAssets = @( + foreach ($Assembly in @($Module.TrackedAssemblies)) { + [ordered]@{ + assemblyName = [string]$Assembly.Name + assemblyVersion = [string]$Assembly.Version + packageVersionCandidate = [string]$Assembly.PackageVersionCandidate + fullName = [string]$Assembly.FullName + sha256 = ([string]$Assembly.Sha256).ToLowerInvariant() + assemblyLoadContext = [string]$Assembly.Alc + isCollectible = [bool]$Assembly.IsCollectible + contributor = [string]$Assembly.ConstituentModule + selectedAsset = ConvertTo-NormalizedEvidencePath -Path ([string]$Assembly.SelectedAssetPath) -ModuleCachePath $ModuleCachePath -RuntimeRoot $RuntimeRoot + } + } + ) + $SelectedAssets = @( + Get-DLLPickleOrdinalSequence -InputObject $UnsortedSelectedAssets -KeySelector { + param($Item) + '{0}{5}{1}{5}{2}{5}{3}{5}{4}' -f $Item.assemblyName, $Item.assemblyVersion, $Item.sha256, $Item.assemblyLoadContext, $Item.selectedAsset, [char]0 + } + ) + [ordered]@{ + name = [string]$Module.Name + umbrellaModule = [string]$Module.UmbrellaModule + constituentModule = [string]$Module.ConstituentModule + version = [string]$Module.Version + latestCompatibleVersion = [string]$Module.LatestCompatibleVersion + repository = [string]$Module.Repository + manifestPowerShellVersion = [string]$Module.ManifestPowerShellVersion + compatiblePSEditions = @(Get-DLLPickleOrdinalSequence -InputObject @($Module.CompatiblePSEditions)) + deterministicProbeCommand = [string]$Module.DeterministicProbeCommand + selectedAssets = $SelectedAssets + } + } +) + +$NormalizedMatrixRows = @( + foreach ($Assembly in @(Get-DLLPickleOrdinalSequence -InputObject @($Matrix.Assemblies) -KeySelector { param($Item) [string]$Item.Name })) { + [ordered]@{ + name = [string]$Assembly.Name + shippedBy = @(Get-DLLPickleOrdinalSequence -InputObject @($Assembly.ShippedBy)) + versions = @(Get-DLLPickleOrdinalSequence -InputObject @($Assembly.Versions)) + hashes = @(Get-DLLPickleOrdinalSequence -InputObject @($Assembly.Hashes | ForEach-Object { ([string]$_).ToLowerInvariant() })) + assemblyLoadContexts = @(Get-DLLPickleOrdinalSequence -InputObject @($Assembly.AlcOwners)) + diverges = [bool]$Assembly.Diverges + selections = @( + foreach ($Selection in @(Get-DLLPickleOrdinalSequence -InputObject @($Assembly.Selections) -KeySelector { + param($Item) + '{0}{4}{1}{4}{2}{4}{3}' -f $Item.Module, $Item.Version, $Item.Sha256, $Item.AlcOwner, [char]0 + })) { + [ordered]@{ + contributor = [string]$Selection.Module + version = [string]$Selection.Version + sha256 = ([string]$Selection.Sha256).ToLowerInvariant() + assemblyLoadContext = [string]$Selection.AlcOwner + } + } + ) + } + } +) + +$NormalizedScenarios = @( + foreach ($Scenario in @(Get-DLLPickleOrdinalSequence -InputObject @($ScenarioEvidence.Scenarios) -KeySelector { + param($Item) + '{0}{3}{1:D10}{3}{2}' -f $Item.ScenarioId, [int]$Item.OrderIndex, [bool]$Item.DllPicklePreloaded, [char]0 + })) { + $FirstAssembly = @($Scenario.Assemblies | Select-Object -First 1) + $ImportedModuleAssets = if ($FirstAssembly.Count -eq 1) { + @( + foreach ($ImportedPath in @($FirstAssembly[0].ImportedModulePaths)) { + ConvertTo-NormalizedEvidencePath -Path ([string]$ImportedPath) -ModuleCachePath $ModuleCachePath -RuntimeRoot $RuntimeRoot + } + ) + } else { + @() + } + [ordered]@{ + scenarioId = [string]$Scenario.ScenarioId + orderIndex = [int]$Scenario.OrderIndex + importOrder = @($Scenario.ImportOrder) + importedModuleAssets = $ImportedModuleAssets + dllPicklePreloaded = [bool]$Scenario.DllPicklePreloaded + expectedLimitation = [bool]$Scenario.ExpectedLimitation + expectedSuccess = $Scenario.ExpectedSuccess + outcomePolicy = [string]$Scenario.OutcomePolicy + probeCommands = @($Scenario.ProbeCommands) + success = [bool]$Scenario.Success + outcomeMatchesExpectation = [bool]$Scenario.OutcomeMatchesExpectation + errorObserved = -not [string]::IsNullOrWhiteSpace([string]$Scenario.Error) + assemblies = @( + $UnsortedScenarioAssemblies = @( + foreach ($Assembly in @($Scenario.Assemblies)) { + [ordered]@{ + name = [string]$Assembly.Name + version = [string]$Assembly.Version + fullName = [string]$Assembly.FullName + sha256 = ([string]$Assembly.Sha256).ToLowerInvariant() + assemblyLoadContext = [string]$Assembly.Alc + isCollectible = [bool]$Assembly.IsCollectible + selectedAsset = ConvertTo-NormalizedEvidencePath -Path ([string]$Assembly.Path) -ModuleCachePath $ModuleCachePath -RuntimeRoot $RuntimeRoot + } + } + ) + Get-DLLPickleOrdinalSequence -InputObject $UnsortedScenarioAssemblies -KeySelector { + param($Item) + '{0}{5}{1}{5}{2}{5}{3}{5}{4}' -f $Item.name, $Item.version, $Item.sha256, $Item.assemblyLoadContext, $Item.selectedAsset, [char]0 + } + ) + } + } +) + +$Content = [ordered]@{ + profile = [ordered]@{ + profileKey = $ProfileKey + powerShellVersion = [string]$Inventory.Profile.PowerShellVersion + powerShellLine = [string]$Inventory.Profile.PowerShellLine + dotNetVersion = [string]$Inventory.Profile.DotNetVersion + dotNetMajor = [int]$Inventory.Profile.DotNetMajor + targetFramework = [string]$Inventory.Profile.TargetFramework + platform = [string]$Inventory.Profile.Platform + architecture = [string]$Inventory.Profile.Architecture + } + validation = [ordered]@{ + deterministicImportNoAuth = [ordered]@{ + status = 'passed' + writesPerformed = $false + conflictSurfaceFingerprint = ([string]$Matrix.Fingerprint).ToLowerInvariant() + scenarioFingerprint = ([string]$ScenarioEvidence.ScenarioFingerprint).ToLowerInvariant() + } + authenticatedReadOnly = [ordered]@{ + status = [string]$ValidationGaps.authenticatedReadOnly + writesPerformed = [bool]$ValidationGaps.writesPerformed + unexecutedCommands = @($ValidationGaps.unexecutedCommands) + } + } + modules = $NormalizedModules + conflictMatrix = [ordered]@{ + conflictSurface = @(Get-DLLPickleOrdinalSequence -InputObject @($Matrix.ConflictSurface)) + assemblies = $NormalizedMatrixRows + } + scenarios = $NormalizedScenarios +} + +$FingerprintEnvelope = [pscustomobject]@{ schemaVersion = 1; content = $Content } +$ContentFingerprint = Get-DLLPickleNormalizedEvidenceFingerprint -Evidence $FingerprintEnvelope +$ObservedOperatingSystemCandidates = @( + @($Inventory.Modules.TrackedAssemblies.OS) + @($ScenarioEvidence.Scenarios.Assemblies.OS) | + Where-Object { -not [string]::IsNullOrWhiteSpace([string]$_) } +) +$ObservedOperatingSystems = @( + Get-DLLPickleOrdinalSequence -InputObject $ObservedOperatingSystemCandidates -Unique +) +if ([string]::IsNullOrWhiteSpace($CapturedAtUtc)) { + $CapturedAtUtc = if (-not [string]::IsNullOrWhiteSpace([string]$Inventory.GeneratedAtUtc)) { + [string]$Inventory.GeneratedAtUtc + } else { + [System.DateTimeOffset]::UtcNow.ToString('o') + } +} +$ParsedCaptureTime = [System.DateTimeOffset]::Parse($CapturedAtUtc).ToUniversalTime().ToString('o') + +$Evidence = [ordered]@{ + schemaVersion = 1 + contentFingerprint = $ContentFingerprint + provenance = [ordered]@{ + sourceRunId = if ([string]::IsNullOrWhiteSpace($SourceRunId)) { $null } else { $SourceRunId } + sourceRunUrl = if ([string]::IsNullOrWhiteSpace($SourceRunUrl)) { $null } else { $SourceRunUrl } + sourceCommitSha = if ([string]::IsNullOrWhiteSpace($SourceCommitSha)) { $null } else { $SourceCommitSha } + capturedAtUtc = $ParsedCaptureTime + observedOperatingSystems = $ObservedOperatingSystems + } + content = $Content +} + +$OutputDirectory = Split-Path -Path $OutputPath -Parent +if ($OutputDirectory -and -not (Test-Path -LiteralPath $OutputDirectory -PathType Container)) { + $null = New-Item -Path $OutputDirectory -ItemType Directory -Force +} +$Evidence | ConvertTo-Json -Depth 100 | Set-Content -LiteralPath $OutputPath -Encoding utf8NoBOM +[pscustomobject]$Evidence diff --git a/tools/New-DLLPicklePowerShellTestMatrix.ps1 b/tools/New-DLLPicklePowerShellTestMatrix.ps1 new file mode 100644 index 00000000..ffbc2ae6 --- /dev/null +++ b/tools/New-DLLPicklePowerShellTestMatrix.ps1 @@ -0,0 +1,59 @@ +<# +.SYNOPSIS +Generate the authoritative exact PowerShell and operating-system CI matrix. + +.PARAMETER MatrixPath +Path to the canonical non-shipped PowerShell test matrix. + +.PARAMETER Compress +Emit compressed JSON for a GitHub Actions job output. + +.OUTPUTS +System.String +#> + +[CmdletBinding()] +[OutputType([string])] +param ( + [Parameter()] + [string]$MatrixPath = (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -ChildPath 'build/powershell-test-matrix.json'), + + [Parameter()] + [switch]$Compress +) + +$ErrorActionPreference = 'Stop' + +if (-not (Test-Path -LiteralPath $MatrixPath -PathType Leaf)) { + throw "PowerShell test matrix not found: $MatrixPath" +} + +$Policy = Get-Content -LiteralPath $MatrixPath -Raw | ConvertFrom-Json -ErrorAction Stop +$Cells = @( + foreach ($RuntimeProfile in @($Policy.profiles)) { + foreach ($Lane in @($Policy.lanes)) { + [ordered]@{ + powerShellVersion = [string]$RuntimeProfile.powerShellVersion + powerShellLine = '{0}.{1}' -f $RuntimeProfile.powerShellMajor, $RuntimeProfile.powerShellMinor + dotnetMajor = [int]$RuntimeProfile.dotnetMajor + targetFramework = [string]$RuntimeProfile.targetFramework + platform = [string]$Lane.platform + runner = [string]$Lane.runner + architecture = [string]$Lane.architecture + provider = [string]$Policy.provisioning.defaultProvider + } + } + } +) + +$ExpectedCellCount = @($Policy.profiles).Count * @($Policy.lanes).Count +if ($ExpectedCellCount -eq 0 -or $Cells.Count -ne $ExpectedCellCount) { + throw "The authoritative DLLPickle runtime matrix must contain exactly $ExpectedCellCount cells; found $($Cells.Count)." +} + +$CellKeys = @($Cells | ForEach-Object { '{0}|{1}|{2}' -f $_.powerShellVersion, $_.platform, $_.architecture }) +if (@($CellKeys | Sort-Object -Unique).Count -ne $Cells.Count) { + throw 'The authoritative DLLPickle runtime matrix contains duplicate cells.' +} + +[ordered]@{ include = $Cells } | ConvertTo-Json -Depth 10 -Compress:$Compress diff --git a/tools/New-DLLPickleProfileEvidenceSummary.ps1 b/tools/New-DLLPickleProfileEvidenceSummary.ps1 new file mode 100644 index 00000000..04f56f15 --- /dev/null +++ b/tools/New-DLLPickleProfileEvidenceSummary.ps1 @@ -0,0 +1,103 @@ +<# +.SYNOPSIS +Aggregates exact-profile upstream baseline comparisons into one stable finding. + +.DESCRIPTION +The summary is deterministic for the same comparison inputs. Its aggregate finding +fingerprint and HTML marker can be used to suppress repeated issue comments without +making this read-only command responsible for GitHub writes. +#> + +[CmdletBinding()] +[OutputType([pscustomobject])] +param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$EvidenceRoot, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$TestMatrixPath = (Join-Path (Split-Path -Parent $PSScriptRoot) 'build/powershell-test-matrix.json'), + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$OutputPath, + + [Parameter()] + [switch]$Strict +) + +$ErrorActionPreference = 'Stop' +if (-not (Test-Path -LiteralPath $TestMatrixPath -PathType Leaf)) { + throw "PowerShell test matrix was not found: $TestMatrixPath" +} +$Matrix = Get-Content -LiteralPath $TestMatrixPath -Raw | ConvertFrom-Json -ErrorAction Stop +$ExpectedProfileKeys = @( + foreach ($RuntimeProfile in @($Matrix.profiles)) { + foreach ($Lane in @($Matrix.lanes)) { + 'ps{0}.{1}-{2}-{3}-{4}' -f $RuntimeProfile.powerShellMajor, $RuntimeProfile.powerShellMinor, $RuntimeProfile.targetFramework, $Lane.platform, $Lane.architecture + } + } +) + +$Comparisons = @( + if (Test-Path -LiteralPath $EvidenceRoot -PathType Container) { + foreach ($ComparisonFile in @(Get-ChildItem -LiteralPath $EvidenceRoot -File -Filter 'baseline-comparison.json' -Recurse | Sort-Object FullName)) { + $Comparison = Get-Content -LiteralPath $ComparisonFile.FullName -Raw | ConvertFrom-Json -ErrorAction Stop + [PSCustomObject]@{ + ProfileKey = [string]$Comparison.ProfileKey + Status = [string]$Comparison.Status + BaselineStatus = [string]$Comparison.BaselineStatus + BaselineFingerprint = [string]$Comparison.BaselineFingerprint + CurrentFingerprint = [string]$Comparison.CurrentFingerprint + BaselineScenarioFingerprint = [string]$Comparison.BaselineScenarioFingerprint + CurrentScenarioFingerprint = [string]$Comparison.CurrentScenarioFingerprint + BaselineEvidenceFingerprint = [string]$Comparison.BaselineEvidenceFingerprint + CurrentEvidenceFingerprint = [string]$Comparison.CurrentEvidenceFingerprint + BaselineEvidencePath = [string]$Comparison.BaselineEvidencePath + FindingFingerprint = [string]$Comparison.FindingFingerprint + SourcePath = [System.IO.Path]::GetRelativePath((Resolve-Path -LiteralPath $EvidenceRoot).Path, $ComparisonFile.FullName).Replace('\', '/') + } + } + } +) +$DuplicateProfileKeys = @($Comparisons | Group-Object ProfileKey | Where-Object Count -GT 1 | ForEach-Object Name) +$MissingProfileKeys = @($ExpectedProfileKeys | Where-Object { $_ -notin $Comparisons.ProfileKey }) +$UnexpectedProfileKeys = @($Comparisons.ProfileKey | Where-Object { $_ -notin $ExpectedProfileKeys }) +$Findings = @($Comparisons | Where-Object Status -NE 'AcceptedUnchanged' | Sort-Object ProfileKey) +$CanonicalText = @( + "missing=$($MissingProfileKeys -join ',')" + "unexpected=$($UnexpectedProfileKeys -join ',')" + "duplicates=$($DuplicateProfileKeys -join ',')" + @($Comparisons | Sort-Object ProfileKey | ForEach-Object { '{0}|{1}|{2}|{3}|{4}|{5}|{6}|{7}|{8}' -f $_.ProfileKey, $_.Status, $_.BaselineFingerprint, $_.CurrentFingerprint, $_.BaselineScenarioFingerprint, $_.CurrentScenarioFingerprint, $_.BaselineEvidenceFingerprint, $_.CurrentEvidenceFingerprint, $_.FindingFingerprint }) +) -join [char]10 +$FingerprintBytes = [System.Text.Encoding]::UTF8.GetBytes($CanonicalText) +$AggregateFingerprint = [System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::HashData($FingerprintBytes)).Replace('-', '').ToLowerInvariant() +$Complete = $Comparisons.Count -eq $ExpectedProfileKeys.Count -and $MissingProfileKeys.Count -eq 0 -and $UnexpectedProfileKeys.Count -eq 0 -and $DuplicateProfileKeys.Count -eq 0 +$Ready = $Complete -and $Findings.Count -eq 0 + +$Summary = [PSCustomObject]@{ + SchemaVersion = 1 + GeneratedAtUtc = [System.DateTimeOffset]::UtcNow.ToString('o') + ExpectedProfileKeys = @($ExpectedProfileKeys) + Comparisons = @($Comparisons | Sort-Object ProfileKey) + MissingProfileKeys = @($MissingProfileKeys) + UnexpectedProfileKeys = @($UnexpectedProfileKeys) + DuplicateProfileKeys = @($DuplicateProfileKeys) + Findings = @($Findings) + AggregateFindingFingerprint = $AggregateFingerprint + FindingMarker = '' -f $AggregateFingerprint + AllProfileEvidencePresent = $Complete + ReadyForCandidateUpdate = $Ready +} + +$OutputDirectory = Split-Path -Path $OutputPath -Parent +if ($OutputDirectory -and -not (Test-Path -LiteralPath $OutputDirectory -PathType Container)) { + $null = New-Item -Path $OutputDirectory -ItemType Directory -Force +} +$Summary | ConvertTo-Json -Depth 30 | Set-Content -LiteralPath $OutputPath -Encoding UTF8 + +if ($Strict.IsPresent -and -not $Ready) { + throw "Exact-profile upstream evidence is not ready: present=$Complete, findings=$($Findings.Count)." +} +$Summary diff --git a/tools/New-DLLPickleRuntimeProfileEvidence.ps1 b/tools/New-DLLPickleRuntimeProfileEvidence.ps1 new file mode 100644 index 00000000..6be37de8 --- /dev/null +++ b/tools/New-DLLPickleRuntimeProfileEvidence.ps1 @@ -0,0 +1,162 @@ +<# +.SYNOPSIS +Capture structured DLLPickle runtime, bundle, assembly, and load-context evidence. + +.PARAMETER PowerShellExecutable +Explicit stock pwsh/pwsh.exe used for the evidence process. + +.PARAMETER PowerShellVersion +Expected exact PowerShell servicing patch. + +.PARAMETER TargetFramework +Expected selected DLLPickle target framework. + +.PARAMETER ModuleManifestPath +Path to the assembled DLLPickle module manifest. + +.PARAMETER OutputPath +Destination JSON evidence path. + +.OUTPUTS +System.IO.FileInfo +#> + +[CmdletBinding()] +[OutputType([System.IO.FileInfo])] +param ( + [Parameter(Mandatory)] + [string]$PowerShellExecutable, + + [Parameter(Mandatory)] + [version]$PowerShellVersion, + + [Parameter(Mandatory)] + [ValidatePattern('^net\d+\.0$')] + [string]$TargetFramework, + + [Parameter(Mandatory)] + [string]$ModuleManifestPath, + + [Parameter(Mandatory)] + [string]$OutputPath +) + +$ErrorActionPreference = 'Stop' +$ResolvedExecutable = (Resolve-Path -LiteralPath $PowerShellExecutable -ErrorAction Stop).Path +$ResolvedManifest = (Resolve-Path -LiteralPath $ModuleManifestPath -ErrorAction Stop).Path +$ModuleRoot = Split-Path -Path $ResolvedManifest -Parent +$SelectedBundlePath = Join-Path -Path (Join-Path -Path $ModuleRoot -ChildPath 'bin') -ChildPath $TargetFramework +if (-not (Test-Path -LiteralPath $SelectedBundlePath -PathType Container)) { + throw "Expected DLLPickle bundle not found: $SelectedBundlePath" +} + +$Payload = [ordered]@{ + manifestPath = $ResolvedManifest + selectedBundlePath = (Resolve-Path -LiteralPath $SelectedBundlePath).Path + targetFramework = $TargetFramework +} +$PayloadBase64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes(($Payload | ConvertTo-Json -Compress))) +$EvidenceProbe = @' +$ErrorActionPreference = 'Stop' +$Payload = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('__PAYLOAD__')) | ConvertFrom-Json +Import-Module -Name $Payload.manifestPath -Force +$ImportResults = @(Import-DPLibrary -SuppressLogo -WarningAction SilentlyContinue) +$BundleRoot = [IO.Path]::GetFullPath($Payload.selectedBundlePath) +$AssemblyEvidence = @( + [AppDomain]::CurrentDomain.GetAssemblies() | + Where-Object { + if ([string]::IsNullOrWhiteSpace($_.Location)) { + return $false + } + $RelativePath = [IO.Path]::GetRelativePath($BundleRoot, [IO.Path]::GetFullPath($_.Location)) + -not $RelativePath.StartsWith('..', [StringComparison]::Ordinal) -and + -not [IO.Path]::IsPathRooted($RelativePath) + } | + Sort-Object -Property FullName -Unique | + ForEach-Object { + $LoadContext = [Runtime.Loader.AssemblyLoadContext]::GetLoadContext($_) + [ordered]@{ + name = $_.GetName().Name + version = $_.GetName().Version.ToString() + fullName = $_.FullName + path = $_.Location + sha256 = (Get-FileHash -LiteralPath $_.Location -Algorithm SHA256).Hash.ToLowerInvariant() + loadContext = if ($LoadContext) { $LoadContext.Name } else { $null } + isCollectible = if ($LoadContext) { $LoadContext.IsCollectible } else { $false } + } + } +) +$Platform = if ([Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([Runtime.InteropServices.OSPlatform]::Windows)) { + 'windows' +} elseif ([Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([Runtime.InteropServices.OSPlatform]::OSX)) { + 'macos' +} else { + 'linux' +} +[ordered]@{ + schemaVersion = 1 + evidenceUtc = [datetime]::UtcNow.ToString('yyyy-MM-ddTHH:mm:ssZ') + runIdentifier = if ($env:GITHUB_RUN_ID) { "$($env:GITHUB_RUN_ID).$($env:GITHUB_RUN_ATTEMPT)" } else { 'local' } + powerShellVersion = $PSVersionTable.PSVersion.ToString() + dotNetVersion = [Environment]::Version.ToString() + dotNetMajor = [Environment]::Version.Major + targetFramework = $Payload.targetFramework + executablePath = [Environment]::ProcessPath + psHome = $PSHOME + platform = $Platform + architecture = [Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant() + selectedBundlePath = $Payload.selectedBundlePath + importResults = $ImportResults + assemblies = $AssemblyEvidence +} | ConvertTo-Json -Depth 12 -Compress +'@.Replace('__PAYLOAD__', $PayloadBase64) + +$ProbeErrorPath = [System.IO.Path]::GetTempFileName() +try { + $ProbeOutput = @(& $ResolvedExecutable -NoLogo -NoProfile -NonInteractive -Command $EvidenceProbe 2> $ProbeErrorPath) + $ProbeExitCode = $LASTEXITCODE + $ProbeError = Get-Content -LiteralPath $ProbeErrorPath -Raw -ErrorAction SilentlyContinue +} finally { + Remove-Item -LiteralPath $ProbeErrorPath -Force -ErrorAction SilentlyContinue +} +if ($ProbeExitCode -ne 0) { + throw "Runtime evidence probe failed: $ProbeError" +} + +try { + $Evidence = $ProbeOutput -join [Environment]::NewLine | ConvertFrom-Json -ErrorAction Stop +} catch { + throw "Runtime evidence probe returned malformed JSON: $($ProbeOutput -join [Environment]::NewLine)" +} +if ([version]$Evidence.powerShellVersion -ne $PowerShellVersion) { + throw "Runtime evidence PowerShell mismatch. Expected $PowerShellVersion but received $($Evidence.powerShellVersion)." +} +if ($Evidence.targetFramework -ne $TargetFramework -or [int]$Evidence.dotNetMajor -ne [int]($TargetFramework -replace '^net|\.0$')) { + throw "Runtime evidence TFM/CLR mismatch for $TargetFramework." +} +if (@($Evidence.importResults | Where-Object Status -eq 'Failed').Count -gt 0) { + throw 'Runtime evidence captured one or more failed DLL imports.' +} +if ([string]$Evidence.selectedBundlePath -cne [string]$Payload.selectedBundlePath) { + throw "Runtime evidence reported an unexpected selected bundle path: '$($Evidence.selectedBundlePath)'." +} +$ObservedAssemblies = @($Evidence.assemblies) +if ($ObservedAssemblies.Count -eq 0) { + throw "Runtime evidence observed no loaded assemblies under the expected '$TargetFramework' bundle." +} +foreach ($Assembly in $ObservedAssemblies) { + $RelativeAssemblyPath = [System.IO.Path]::GetRelativePath( + [string]$Payload.selectedBundlePath, + [System.IO.Path]::GetFullPath([string]$Assembly.path) + ) + if ($RelativeAssemblyPath.StartsWith('..', [System.StringComparison]::Ordinal) -or [System.IO.Path]::IsPathRooted($RelativeAssemblyPath)) { + throw "Runtime evidence observed an assembly outside the expected '$TargetFramework' bundle: $($Assembly.path)" + } +} + +$OutputDirectory = Split-Path -Path $OutputPath -Parent +if (-not [string]::IsNullOrWhiteSpace($OutputDirectory) -and -not (Test-Path -LiteralPath $OutputDirectory -PathType Container)) { + $null = New-Item -Path $OutputDirectory -ItemType Directory -Force +} +$Evidence | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath $OutputPath -Encoding utf8 +Get-Item -LiteralPath $OutputPath diff --git a/tools/New-DLLPickleSupportDocumentation.ps1 b/tools/New-DLLPickleSupportDocumentation.ps1 new file mode 100644 index 00000000..60ddb87d --- /dev/null +++ b/tools/New-DLLPickleSupportDocumentation.ps1 @@ -0,0 +1,215 @@ +<# +.SYNOPSIS + Generates support and upstream-compatibility documentation from policy data. + +.DESCRIPTION + Produces the human-readable Microsoft support matrix and the profile-aware + upstream evidence register. Pending CI or credential-dependent evidence is + rendered as a gap and is never represented as passing. + +.PARAMETER Check + Compare generated content with committed files and throw on drift without writing. +#> + +[CmdletBinding()] +param( + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$SupportPolicyPath = (Join-Path (Split-Path -Parent $PSScriptRoot) 'src/DLLPickle/SupportedRuntimeProfiles.json'), + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$TestMatrixPath = (Join-Path (Split-Path -Parent $PSScriptRoot) 'build/powershell-test-matrix.json'), + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$DependencyPolicyPath = (Join-Path (Split-Path -Parent $PSScriptRoot) 'build/dependency-policy.json'), + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$OutputDirectory = (Join-Path (Split-Path -Parent $PSScriptRoot) 'docs/generated'), + + [Parameter()] + [switch]$Check +) + +$ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'DLLPickle.ProfileEvidence.ps1') + +foreach ($RequiredPath in @($SupportPolicyPath, $TestMatrixPath, $DependencyPolicyPath)) { + if (-not (Test-Path -LiteralPath $RequiredPath -PathType Leaf)) { + throw "Required documentation source was not found: $RequiredPath" + } +} + +$SupportPolicy = Get-Content -LiteralPath $SupportPolicyPath -Raw | ConvertFrom-Json -ErrorAction Stop +$TestMatrix = Get-Content -LiteralPath $TestMatrixPath -Raw | ConvertFrom-Json -ErrorAction Stop +$DependencyPolicy = Get-Content -LiteralPath $DependencyPolicyPath -Raw | ConvertFrom-Json -ErrorAction Stop +$DependencyPolicyDirectory = Split-Path -Path (Resolve-Path -LiteralPath $DependencyPolicyPath).Path -Parent + +$ShippedProfileKeys = @($SupportPolicy.profiles | ForEach-Object { '{0}.{1}|{2}|{3}' -f $_.powerShellMajor, $_.powerShellMinor, $_.dotnetMajor, $_.targetFramework }) +$TestProfileKeys = @($TestMatrix.profiles | ForEach-Object { '{0}.{1}|{2}|{3}' -f $_.powerShellMajor, $_.powerShellMinor, $_.dotnetMajor, $_.targetFramework }) +if (Compare-Object -ReferenceObject $ShippedProfileKeys -DifferenceObject $TestProfileKeys) { + throw 'Shipped runtime profiles and documentation test profiles do not align.' +} +$VerifiedDate = (ConvertTo-DLLPickleUtcDateTimeOffset -Value $TestMatrix.lastVerifiedUtc).ToString('yyyy-MM-dd') +$NewLine = "`r`n" + +$SupportLines = [System.Collections.Generic.List[string]]::new() +$SupportLines.Add('# Supported PowerShell runtime matrix') +$SupportLines.Add('') +$SupportLines.Add('') +$SupportLines.Add('') +$SupportLines.Add("Lifecycle and servicing data last verified: **$VerifiedDate**.") +$SupportLines.Add('') +$SupportLines.Add('| PowerShell line | Exact CI patch | .NET runtime | Bundled TFM | Microsoft lifecycle | Support end | Required OS cells |') +$SupportLines.Add('|---|---:|---:|---:|---|---:|---|') +foreach ($MatrixProfile in @($TestMatrix.profiles)) { + $PowerShellLine = '{0}.{1}' -f $MatrixProfile.powerShellMajor, $MatrixProfile.powerShellMinor + $SupportRow = '| {0} | {1} | {2} | `{3}` | {4} | {5} | Windows, Linux, macOS |' -f $PowerShellLine, $MatrixProfile.powerShellVersion, $MatrixProfile.dotnetRuntimeVersion, $MatrixProfile.targetFramework, $MatrixProfile.lifecycleState, $MatrixProfile.lifecycleEndDate + $SupportLines.Add($SupportRow) +} +$SupportLines.Add('') +$SupportLines.Add('This table is the Microsoft-supported runtime contract used by the loader and CI. Upstream module compatibility is a separate evidence question; see [Upstream compatibility evidence](Compatibility-Evidence.md). Exact servicing patches are test pins, not minimum patch claims for end users.') +$SupportLines.Add('') +$SupportLines.Add('The module selects a bundle using both the PowerShell minor line and CLR major, then fails closed on a mismatched or unknown pair. Only the rows above are part of the current support contract.') +$SupportLines.Add('') +$SupportLines.Add('`multi-pwsh` is optional, checksum-pinned CI provisioning infrastructure. CI invokes the provisioned official `pwsh` executable directly. No multi-pwsh executable, package, manifest dependency, runtime reference, or license payload is published with DLLPickle.') + +$EvidenceLines = [System.Collections.Generic.List[string]]::new() +$EvidenceLines.Add('# Upstream compatibility evidence') +$EvidenceLines.Add('') +$EvidenceLines.Add('') +$EvidenceLines.Add('') +$EvidenceLines.Add('Microsoft runtime support and upstream module behavior are tracked independently. The rows below deliberately remain release-gating gaps until a profile-specific CI run is accepted; a supported PowerShell line does not imply that every upstream module combination can coexist in one process.') +$EvidenceLines.Add('') +$EvidenceLines.Add('| Module | Version | PowerShell | TFM | OS | Selected asset | Assembly / ALC result | Verdict | Evidence date | Run ID |') +$EvidenceLines.Add('|---|---|---:|---:|---|---|---|---|---:|---|') +foreach ($RuntimeProfile in @($DependencyPolicy.runtimeProfiles)) { + foreach ($Platform in @($RuntimeProfile.platforms)) { + $PlatformBaseline = $RuntimeProfile.baselines.$Platform + $Verdict = if ($PlatformBaseline) { [string]$PlatformBaseline.status } else { 'missing-baseline' } + $Evidence = $null + $EvidenceDate = $VerifiedDate + $RunReference = 'not-run' + if ($Verdict -eq 'accepted') { + if ([string]::IsNullOrWhiteSpace([string]$PlatformBaseline.evidencePath) -or + [string]::IsNullOrWhiteSpace([string]$PlatformBaseline.evidenceFingerprint)) { + throw "Accepted $Platform evidence for PowerShell $($RuntimeProfile.powerShellLine) has no committed path or fingerprint." + } + $EvidencePath = Join-Path -Path $DependencyPolicyDirectory -ChildPath ([string]$PlatformBaseline.evidencePath) + if (-not (Test-Path -LiteralPath $EvidencePath -PathType Leaf)) { + throw "Accepted profile evidence was not found: $EvidencePath" + } + $Evidence = Get-Content -LiteralPath $EvidencePath -Raw | ConvertFrom-Json -ErrorAction Stop + $RecomputedEvidenceFingerprint = Get-DLLPickleNormalizedEvidenceFingerprint -Evidence $Evidence + if ([string]$Evidence.contentFingerprint -ne $RecomputedEvidenceFingerprint -or + [string]$PlatformBaseline.evidenceFingerprint -ne $RecomputedEvidenceFingerprint) { + throw "Accepted profile evidence does not recompute to the policy fingerprint: $EvidencePath" + } + $PlatformLanes = @($TestMatrix.lanes | Where-Object platform -EQ $Platform) + if ($PlatformLanes.Count -ne 1 -or [string]::IsNullOrWhiteSpace([string]$PlatformLanes[0].architecture)) { + throw "The test matrix must declare exactly one architecture-bearing lane for '$Platform'." + } + $ExpectedProfileKey = 'ps{0}-{1}-{2}-{3}' -f $RuntimeProfile.powerShellLine, $RuntimeProfile.targetFramework, $Platform, $PlatformLanes[0].architecture + if ([string]$Evidence.content.profile.profileKey -ne $ExpectedProfileKey) { + throw "Accepted profile evidence '$($Evidence.content.profile.profileKey)' does not match '$ExpectedProfileKey'." + } + $EvidenceDate = (ConvertTo-DLLPickleUtcDateTimeOffset -Value $Evidence.provenance.capturedAtUtc).ToString('yyyy-MM-dd') + $SourceRunId = [string]$Evidence.provenance.sourceRunId + $SourceRunUrl = [string]$Evidence.provenance.sourceRunUrl + $RunReference = if (-not [string]::IsNullOrWhiteSpace($SourceRunId) -and -not [string]::IsNullOrWhiteSpace($SourceRunUrl)) { + '[{0}]({1})' -f $SourceRunId, $SourceRunUrl + } elseif (-not [string]::IsNullOrWhiteSpace($SourceRunId)) { + $SourceRunId + } else { + 'committed evidence' + } + } + foreach ($Module in @($DependencyPolicy.monitoredModules)) { + if ($Evidence) { + $EvidenceModules = @($Evidence.content.modules | Where-Object name -eq $Module.name) + if ($EvidenceModules.Count -ne 1) { + throw "Accepted profile evidence for '$($Evidence.content.profile.profileKey)' does not contain exactly one '$($Module.name)' module row." + } + $EvidenceModule = $EvidenceModules[0] + $SelectedAssetText = if (@($EvidenceModule.selectedAssets).Count -eq 0) { + 'no tracked assembly selected' + } else { + @($EvidenceModule.selectedAssets | ForEach-Object { '`{0}`' -f $_.selectedAsset }) -join '
' + } + $AssemblyResultText = if (@($EvidenceModule.selectedAssets).Count -eq 0) { + 'no tracked assembly observed' + } else { + @( + $EvidenceModule.selectedAssets | + ForEach-Object { '`{0}` {1} / `{2}`' -f $_.assemblyName, $_.assemblyVersion, $_.assemblyLoadContext } + ) -join '
' + } + $EvidenceRow = '| {0} | {1} | {2} | `{3}` | {4} | {5} | {6} | **{7}** | {8} | {9} |' -f $Module.name, $EvidenceModule.version, $RuntimeProfile.powerShellLine, $RuntimeProfile.targetFramework, $Platform, $SelectedAssetText, $AssemblyResultText, $Verdict, $EvidenceDate, $RunReference + } else { + $EvidenceRow = '| {0} | pending exact-profile inventory | {1} | `{2}` | {3} | pending CI artifact | pending hash and ALC | **{4}** | {5} | not-run |' -f $Module.name, $RuntimeProfile.powerShellLine, $RuntimeProfile.targetFramework, $Platform, $Verdict, $VerifiedDate + } + $EvidenceLines.Add($EvidenceRow) + } + } +} +$EvidenceLines.Add('') +$EvidenceLines.Add('## Known process-isolation requirement') +$EvidenceLines.Add('') +$EvidenceLines.Add('Issue #174 remains an expected limitation: ExchangeOnlineManagement and Az.Storage can require incompatible Microsoft.OData major versions. Both import orders are tested, but separate PowerShell processes remain the documented safe boundary until fresh evidence supports a narrower rule.') +$EvidenceLines.Add('') +$EvidenceLines.Add('## Preserved regression coverage') +$EvidenceLines.Add('') +$EvidenceLines.Add('- Issue #34: a deterministic negative-control/protected scenario verifies that Microsoft Graph authentication can bind `BaseAbstractApplicationBuilder.WithLogging(IIdentityLogger, Boolean)` after DLLPickle preloading.') +$EvidenceLines.Add('- Issue #193: package and runtime checks keep incidental `Microsoft.Extensions.*` assemblies out of the preload bundle.') +$EvidenceLines.Add('- PR #215: package, runtime, and ALC checks keep `Azure.Core` and `System.ClientModel` out of DLLPickle so Az.Accounts retains ownership of its private Azure SDK load context.') +$EvidenceLines.Add('- Issue #242: a worker-thread assembly-load scenario verifies that DLLPickle does not install PowerShell script-block assembly callbacks that can crash the process.') +$EvidenceLines.Add('') +$EvidenceLines.Add('## Authenticated read-only release gates') +$EvidenceLines.Add('') +$EvidenceLines.Add('The deterministic no-auth tier runs in CI. The following credential-dependent commands are not executed without approved credentials and must not be represented as passing:') +$EvidenceLines.Add('') +foreach ($Module in @($DependencyPolicy.monitoredModules)) { + $GapRow = '- `{0}`: `{1}`' -f $Module.name, $Module.authenticatedReadOnlyProbeCommand + $EvidenceLines.Add($GapRow) +} +$EvidenceLines.Add('') +$EvidenceLines.Add('These probes permit reads only; writes are not part of the validation tier. PowerShellEditorServices / VS Code coverage for issue #169 also remains an explicit manual gap unless a run artifact records it.') +$EvidenceLines.Add('') +$EvidenceLines.Add('Before the protected credentialed workflow exists, the release gate may accept one explicitly reviewed manual transition record for version `3.0.0`. That record must match the exact bundle-source fingerprint, cover the three exact Windows profiles and fixed read-only scenarios, contain no credential material or raw service output, record zero writes, and expire exactly 14 days after capture starts. It is transitional compatibility evidence, not least-privilege workload-identity proof.') + +$Documents = [ordered]@{ + 'Support-Matrix.md' = ($SupportLines -join $NewLine) + $NewLine + 'Compatibility-Evidence.md' = ($EvidenceLines -join $NewLine) + $NewLine +} + +if ($Check.IsPresent) { + $Drift = [System.Collections.Generic.List[string]]::new() + foreach ($Document in $Documents.GetEnumerator()) { + $Path = Join-Path $OutputDirectory $Document.Key + if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { + $Drift.Add("Missing generated document: $Path") + continue + } + $Actual = Get-Content -LiteralPath $Path -Raw + if ($Actual -ne $Document.Value) { + $Drift.Add("Generated document is stale: $Path") + } + } + if ($Drift.Count -gt 0) { + throw ($Drift -join ' ') + } +} else { + if (-not (Test-Path -LiteralPath $OutputDirectory -PathType Container)) { + $null = New-Item -Path $OutputDirectory -ItemType Directory -Force + } + foreach ($Document in $Documents.GetEnumerator()) { + Set-Content -LiteralPath (Join-Path $OutputDirectory $Document.Key) -Value $Document.Value -Encoding UTF8 -NoNewline + } +} + +[PSCustomObject]@{ + Checked = $Check.IsPresent + Documents = @($Documents.Keys | ForEach-Object { Join-Path $OutputDirectory $_ }) +} diff --git a/tools/New-DLLPickleUpstreamScenarioEvidence.ps1 b/tools/New-DLLPickleUpstreamScenarioEvidence.ps1 new file mode 100644 index 00000000..74c79d3f --- /dev/null +++ b/tools/New-DLLPickleUpstreamScenarioEvidence.ps1 @@ -0,0 +1,251 @@ +<# +.SYNOPSIS +Executes deterministic upstream import orders with and without DLLPickle. + +.DESCRIPTION +Uses an exact stock PowerShell executable, exact saved module manifests, and an +isolated module path. Every configured import order runs twice in a fresh process: +without DLLPickle and after DLLPickle preloading. The report captures selected +assemblies and ALC ownership and emits a stable profile-keyed scenario fingerprint. +Known process-isolation limitations are observational in this no-auth tier: either +outcome is recorded, but a successful no-auth probe does not clear a limitation +whose re-adjudication requires authenticated runtime evidence. +#> + +[CmdletBinding()] +[OutputType([pscustomobject])] +param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$PolicyPath, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$InventoryPath, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$PowerShellExecutable, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$DLLPickleManifestPath, + + [Parameter()] + [string]$KnownConflictsPath, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$OutputPath, + + [Parameter()] + [switch]$Strict +) + +$ErrorActionPreference = 'Stop' +foreach ($Path in @($PolicyPath, $InventoryPath, $PowerShellExecutable, $DLLPickleManifestPath)) { + if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { + throw "Required upstream scenario input was not found: $Path" + } +} + +$ResolvedPolicyPath = (Resolve-Path -LiteralPath $PolicyPath).Path +$ResolvedInventoryPath = (Resolve-Path -LiteralPath $InventoryPath).Path +$ResolvedPowerShellExecutable = (Resolve-Path -LiteralPath $PowerShellExecutable).Path +$ResolvedDLLPickleManifestPath = (Resolve-Path -LiteralPath $DLLPickleManifestPath).Path +$Policy = Get-Content -LiteralPath $ResolvedPolicyPath -Raw | ConvertFrom-Json -ErrorAction Stop +$Inventory = Get-Content -LiteralPath $ResolvedInventoryPath -Raw | ConvertFrom-Json -ErrorAction Stop +if (-not $Inventory.Profile -or [string]::IsNullOrWhiteSpace([string]$Inventory.ProfileKey)) { + throw 'The upstream inventory is not keyed to an exact runtime profile.' +} +$ProfileKeyValues = @( + [string]$Inventory.Profile.PowerShellLine + [string]$Inventory.Profile.TargetFramework + [string]$Inventory.Profile.Platform + [string]$Inventory.Profile.Architecture +) +if (@($ProfileKeyValues | Where-Object { [string]::IsNullOrWhiteSpace($_) }).Count -gt 0) { + throw "Upstream inventory '$($Inventory.ProfileKey)' lacks a complete PowerShell line, TFM, platform, and architecture profile." +} +$DerivedProfileKey = 'ps{0}-{1}-{2}-{3}' -f $ProfileKeyValues +if ([string]$Inventory.ProfileKey -ne $DerivedProfileKey) { + throw "Upstream inventory profile key '$($Inventory.ProfileKey)' does not match derived profile key '$DerivedProfileKey'." +} + +$ProfilePolicy = @($Policy.runtimeProfiles | Where-Object { + $_.powerShellLine -eq $Inventory.Profile.PowerShellLine -and + $_.targetFramework -eq $Inventory.Profile.TargetFramework + }) +if ($ProfilePolicy.Count -ne 1) { + throw "No unique dependency-policy profile matches '$($Inventory.ProfileKey)'." +} +$ModulePolicyByName = @{} +foreach ($ModulePolicy in @($Policy.monitoredModules)) { + $ModulePolicyByName[[string]$ModulePolicy.name] = $ModulePolicy +} +$InventoryModuleByName = @{} +foreach ($Module in @($Inventory.Modules)) { + $InventoryModuleByName[[string]$Module.Name] = $Module +} +$ModuleSearchPath = @([string]$Inventory.ModuleCachePath, (Join-Path -Path ([string]$Inventory.Profile.PSHome) -ChildPath 'Modules')) +$SnapshotScriptPath = Join-Path $PSScriptRoot 'Get-DLLPickleRuntimeAssemblySnapshot.ps1' +$ScenarioDefinitions = [System.Collections.Generic.List[object]]::new() +$PolicyOrderIndex = 0 +foreach ($ImportOrder in @($ProfilePolicy[0].importOrders)) { + $PolicyOrderIndex++ + $ScenarioDefinitions.Add([PSCustomObject]@{ + ScenarioId = 'profile-target-scenario-{0:d2}' -f $PolicyOrderIndex + ImportOrder = @($ImportOrder) + ExpectedLimitation = $false + ExpectedSuccess = $true + OutcomePolicy = 'must-succeed' + }) +} +if (-not [string]::IsNullOrWhiteSpace($KnownConflictsPath)) { + if (-not (Test-Path -LiteralPath $KnownConflictsPath -PathType Leaf)) { + throw "Known-conflicts policy was not found: $KnownConflictsPath" + } + $KnownConflicts = @(Get-Content -LiteralPath $KnownConflictsPath -Raw | ConvertFrom-Json -ErrorAction Stop) + foreach ($KnownConflict in @($KnownConflicts | Where-Object { [string]$_.id -in @($ProfilePolicy[0].knownConflictIds) })) { + $ExpectedLimitation = [bool]$KnownConflict.requiresProcessIsolation + foreach ($ImportOrder in @($KnownConflict.importOrders)) { + $ScenarioDefinitions.Add([PSCustomObject]@{ + ScenarioId = [string]$KnownConflict.id + ImportOrder = @($ImportOrder) + ExpectedLimitation = $ExpectedLimitation + ExpectedSuccess = if ($ExpectedLimitation) { $null } else { $true } + OutcomePolicy = if ($ExpectedLimitation) { 'observe-known-limitation' } else { 'must-succeed' } + }) + } + } +} + +$ScenarioResults = [System.Collections.Generic.List[object]]::new() +$OrderIndex = 0 +foreach ($ScenarioDefinition in $ScenarioDefinitions) { + $OrderIndex++ + $ImportOrder = @($ScenarioDefinition.ImportOrder | ForEach-Object { [string]$_ }) + $ManifestPaths = @( + foreach ($ModuleName in $ImportOrder) { + if (-not $InventoryModuleByName.ContainsKey($ModuleName)) { + throw "Upstream inventory '$($Inventory.ProfileKey)' does not contain module '$ModuleName'." + } + $ManifestPath = [string]$InventoryModuleByName[$ModuleName].ModuleManifestPath + if ([string]::IsNullOrWhiteSpace($ManifestPath)) { + throw "Upstream inventory module '$ModuleName' has no exact manifest path." + } + $ManifestPath + } + ) + $ProbeCommands = @( + foreach ($ModuleName in $ImportOrder) { + if (-not $ModulePolicyByName.ContainsKey($ModuleName)) { + throw "Dependency policy has no monitored-module row for '$ModuleName'." + } + [string]$ModulePolicyByName[$ModuleName].deterministicProbeCommand + } + ) + $CombinedProbeCommand = @($ProbeCommands | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) -join '; ' + + foreach ($PreloadDllPickle in @($false, $true)) { + $Scenario = [ordered]@{ + ScenarioId = [string]$ScenarioDefinition.ScenarioId + OrderIndex = $OrderIndex + ImportOrder = @($ImportOrder) + DllPicklePreloaded = $PreloadDllPickle + ExpectedLimitation = [bool]$ScenarioDefinition.ExpectedLimitation + ExpectedSuccess = $ScenarioDefinition.ExpectedSuccess + OutcomePolicy = [string]$ScenarioDefinition.OutcomePolicy + ProbeCommands = @($ProbeCommands) + Success = $false + OutcomeMatchesExpectation = $false + Assemblies = @() + Error = $null + } + try { + $SnapshotParameters = @{ + ModuleName = $ImportOrder + ModuleManifestPath = $ManifestPaths + ModuleSearchPath = $ModuleSearchPath + ProbeCommand = $CombinedProbeCommand + PolicyPath = $ResolvedPolicyPath + PowerShellExecutable = $ResolvedPowerShellExecutable + PowerShellVersion = [version]$Inventory.Profile.PowerShellVersion + TargetFramework = [string]$Inventory.Profile.TargetFramework + Strict = $true + } + if ($PreloadDllPickle) { + $SnapshotParameters['PreloadDllPickleManifest'] = $ResolvedDLLPickleManifestPath + } + $Scenario.Assemblies = @(& $SnapshotScriptPath @SnapshotParameters) + $Scenario.Success = $true + } catch { + $Scenario.Error = $_.Exception.Message + } + $Scenario.OutcomeMatchesExpectation = + $Scenario.OutcomePolicy -eq 'observe-known-limitation' -or + $Scenario.Success -eq $Scenario.ExpectedSuccess + $ScenarioResults.Add([PSCustomObject]$Scenario) + } +} + +$ObservedAssemblies = @( + foreach ($ScenarioResult in $ScenarioResults) { + foreach ($Assembly in @($ScenarioResult.Assemblies)) { + $Assembly + } + } +) +if ($ObservedAssemblies.Count -eq 0) { + throw "Deterministic upstream scenarios for '$($Inventory.ProfileKey)' contain no observed tracked assemblies." +} +foreach ($ObservedAssembly in $ObservedAssemblies) { + if ([string]$ObservedAssembly.Platform -ne [string]$Inventory.Profile.Platform -or + [string]$ObservedAssembly.Architecture -ne [string]$Inventory.Profile.Architecture) { + throw "Scenario assembly '$($ObservedAssembly.Name)' was observed on '$($ObservedAssembly.Platform)/$($ObservedAssembly.Architecture)', expected '$($Inventory.Profile.Platform)/$($Inventory.Profile.Architecture)' for '$($Inventory.ProfileKey)'." + } +} + +$CanonicalRows = @( + "profile=$($Inventory.ProfileKey)" + foreach ($Scenario in @($ScenarioResults | Sort-Object OrderIndex,DllPicklePreloaded)) { + 'scenario={0}|order={1}|preload={2}|expectedLimitation={3}|expectedSuccess={4}|outcomePolicy={5}|success={6}|outcomeMatches={7}|error={10}|modules={8}|assemblies={9}' -f ( + $Scenario.ScenarioId, + $Scenario.OrderIndex, + $Scenario.DllPicklePreloaded, + $Scenario.ExpectedLimitation, + $Scenario.ExpectedSuccess, + $Scenario.OutcomePolicy, + $Scenario.Success, + $Scenario.OutcomeMatchesExpectation, + (@($Scenario.ImportOrder) -join ','), + (@($Scenario.Assemblies | Sort-Object Name,Path | ForEach-Object { '{0},{1},{2},{3},{4}' -f $_.Name, $_.Version, $_.Sha256, $_.Path, $_.Alc }) -join ';'), + (([string]$Scenario.Error) -replace '(?i)dpp-snap-[0-9a-f]{32}\.(ps1|json)', 'dpp-snap-.$1' -replace '\s+', ' ').Trim() + ) + } +) +$FingerprintBytes = [System.Text.Encoding]::UTF8.GetBytes(($CanonicalRows -join [char]10)) +$ScenarioFingerprint = [System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::HashData($FingerprintBytes)).Replace('-', '').ToLowerInvariant() +$Report = [PSCustomObject]@{ + SchemaVersion = 1 + GeneratedAtUtc = [System.DateTimeOffset]::UtcNow.ToString('o') + ProfileKey = [string]$Inventory.ProfileKey + Profile = $Inventory.Profile + ValidationTier = 'deterministic-import-no-auth' + WritesPerformed = $false + ScenarioFingerprint = $ScenarioFingerprint + Scenarios = @($ScenarioResults) + Passed = @($ScenarioResults | Where-Object { -not $_.OutcomeMatchesExpectation }).Count -eq 0 +} + +$OutputDirectory = Split-Path -Path $OutputPath -Parent +if ($OutputDirectory -and -not (Test-Path -LiteralPath $OutputDirectory -PathType Container)) { + $null = New-Item -Path $OutputDirectory -ItemType Directory -Force +} +$Report | ConvertTo-Json -Depth 40 | Set-Content -LiteralPath $OutputPath -Encoding UTF8 + +if ($Strict.IsPresent -and -not $Report.Passed) { + $FailedLabels = @($ScenarioResults | Where-Object { -not $_.OutcomeMatchesExpectation } | ForEach-Object { "order $($_.OrderIndex), preload=$($_.DllPicklePreloaded), policy=$($_.OutcomePolicy), expectedSuccess=$($_.ExpectedSuccess), actualSuccess=$($_.Success)" }) + throw "Deterministic upstream scenarios failed for '$($Inventory.ProfileKey)': $($FailedLabels -join '; ')." +} +$Report diff --git a/tools/Set-DLLPickleManualAuthenticatedEvidenceAcceptance.ps1 b/tools/Set-DLLPickleManualAuthenticatedEvidenceAcceptance.ps1 new file mode 100644 index 00000000..a672d090 --- /dev/null +++ b/tools/Set-DLLPickleManualAuthenticatedEvidenceAcceptance.ps1 @@ -0,0 +1,73 @@ +<# +.SYNOPSIS +Records explicit maintainer acceptance of a validated manual evidence candidate. + +.DESCRIPTION +Validates the pending candidate, adds only acceptance metadata, writes the +committable transition file, and immediately revalidates it in Release mode. +This command does not commit, push, release, or publish anything. +#> + +[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] +[OutputType([pscustomobject])] +param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$CandidateEvidencePath, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$OutputPath = (Join-Path (Split-Path -Path $PSScriptRoot -Parent) 'build/authenticated-evidence/initial-multitarget-major.json'), + + [Parameter(Mandatory)] + [ValidatePattern('^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$')] + [string]$AcceptedBy, + + [Parameter(Mandatory)] + [ValidateSet('low', 'medium', 'high')] + [string]$Confidence, + + [Parameter()] + [System.DateTimeOffset]$AcceptedAtUtc = [System.DateTimeOffset]::UtcNow +) + +$ErrorActionPreference = 'Stop' +$RepositoryRoot = Split-Path -Path $PSScriptRoot -Parent +$ValidatorPath = Join-Path $PSScriptRoot 'Test-DLLPickleManualAuthenticatedEvidence.ps1' +$ValidationParameters = @{ + EvidencePath = $CandidateEvidencePath + RepositoryRoot = $RepositoryRoot + TestMatrixPath = (Join-Path $RepositoryRoot 'build/powershell-test-matrix.json') + DependencyPolicyPath = (Join-Path $RepositoryRoot 'build/dependency-policy.json') + Mode = 'Capture' + NowUtc = $AcceptedAtUtc +} +$null = & $ValidatorPath @ValidationParameters +$Evidence = Get-Content -LiteralPath $CandidateEvidencePath -Raw | ConvertFrom-Json -ErrorAction Stop +$Evidence.acceptance.status = 'accepted' +$Evidence.acceptance.acceptedAtUtc = $AcceptedAtUtc.ToUniversalTime().ToString('o') +$Evidence.acceptance.acceptedBy = $AcceptedBy +$Evidence.acceptance.confidence = $Confidence + +if (-not $PSCmdlet.ShouldProcess($OutputPath, "Record accepted manual authenticated evidence for version $($Evidence.content.bridge.allowedReleaseVersion)")) { + return +} +$OutputDirectory = Split-Path -Path $OutputPath -Parent +if ($OutputDirectory -and -not (Test-Path -LiteralPath $OutputDirectory -PathType Container)) { + $null = New-Item -Path $OutputDirectory -ItemType Directory -Force +} +$Evidence | ConvertTo-Json -Depth 100 | Set-Content -LiteralPath $OutputPath -Encoding utf8NoBOM +$ValidationParameters.EvidencePath = $OutputPath +$ValidationParameters.Mode = 'Release' +$Validated = & $ValidatorPath @ValidationParameters +[pscustomobject]@{ + OutputPath = [System.IO.Path]::GetFullPath($OutputPath) + AcceptedBy = $AcceptedBy + Confidence = $Confidence + AcceptedAtUtc = $AcceptedAtUtc.ToUniversalTime().ToString('o') + EvidenceFingerprint = [string]$Validated.EvidenceFingerprint + BundleSourceFingerprint = [string]$Validated.BundleSourceFingerprint + AllowedReleaseVersion = [string]$Validated.AllowedReleaseVersion + ExpiresAtUtc = [string]$Validated.ExpiresAtUtc + WritesPerformed = $false +} diff --git a/tools/Test-DLLPickleFindingFingerprintReported.ps1 b/tools/Test-DLLPickleFindingFingerprintReported.ps1 new file mode 100644 index 00000000..eca93588 --- /dev/null +++ b/tools/Test-DLLPickleFindingFingerprintReported.ps1 @@ -0,0 +1,27 @@ +<# +.SYNOPSIS +Tests whether a stable DLLPickle finding fingerprint is already present in report text. + +.DESCRIPTION +This deterministic helper recognizes only the exact HTML marker used by scheduled +GitHub issue bodies and comments. It performs no network access or external writes. +#> + +[CmdletBinding()] +[OutputType([bool])] +param ( + [Parameter(Mandatory)] + [ValidatePattern('^[a-fA-F0-9]{64}$')] + [string]$Fingerprint, + + [Parameter()] + [AllowEmptyCollection()] + [AllowEmptyString()] + [string[]]$Text = @() +) + +$Marker = '' -f $Fingerprint.ToLowerInvariant() +return @($Text | Where-Object { + -not [string]::IsNullOrWhiteSpace($_) -and + $_.Contains($Marker, [System.StringComparison]::OrdinalIgnoreCase) + }).Count -gt 0 diff --git a/tools/Test-DLLPickleManualAuthenticatedEvidence.ps1 b/tools/Test-DLLPickleManualAuthenticatedEvidence.ps1 new file mode 100644 index 00000000..685efd9d --- /dev/null +++ b/tools/Test-DLLPickleManualAuthenticatedEvidence.ps1 @@ -0,0 +1,317 @@ +<# +.SYNOPSIS +Validates the time-bounded manual authenticated-evidence transition record. + +.DESCRIPTION +This validator is deliberately narrow. It accepts only the initial 3.0.0 +PowerShell 7.4-7.6 multi-target release, exactly three Windows x64 profiles, +hard-coded read-only probes, and evidence bound to the current published-bundle +source fingerprint. It is not a generic authentication bypass and does not +replace the future protected credentialed workflow. + +.PARAMETER EvidencePath +Path to the sanitized manual evidence JSON. + +.PARAMETER RepositoryRoot +Repository root whose published inputs must match the evidence fingerprint. + +.PARAMETER TestMatrixPath +Exact supported PowerShell runtime matrix. + +.PARAMETER DependencyPolicyPath +Profile and monitored-module policy. + +.PARAMETER Mode +Capture validates a pending candidate. Release additionally requires maintainer +acceptance and a currently valid expiry window. + +.PARAMETER NowUtc +Clock injection for deterministic expiry tests. + +.OUTPUTS +System.Management.Automation.PSCustomObject +#> + +[CmdletBinding()] +[OutputType([pscustomobject])] +param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$EvidencePath, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$RepositoryRoot = (Split-Path -Path $PSScriptRoot -Parent), + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$TestMatrixPath = (Join-Path (Split-Path -Path $PSScriptRoot -Parent) 'build/powershell-test-matrix.json'), + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$DependencyPolicyPath = (Join-Path (Split-Path -Path $PSScriptRoot -Parent) 'build/dependency-policy.json'), + + [Parameter()] + [ValidateSet('Capture', 'Release')] + [string]$Mode = 'Release', + + [Parameter()] + [System.DateTimeOffset]$NowUtc = [System.DateTimeOffset]::UtcNow +) + +$ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'DLLPickle.ProfileEvidence.ps1') + +function Assert-ExactStringSet { + param( + [Parameter(Mandatory)][AllowEmptyCollection()][string[]]$Actual, + [Parameter(Mandatory)][AllowEmptyCollection()][string[]]$Expected, + [Parameter(Mandatory)][string]$Label + ) + + $ExpectedSorted = @(Get-DLLPickleOrdinalSequence -InputObject @($Expected)) + $ActualSorted = @(Get-DLLPickleOrdinalSequence -InputObject @($Actual)) + $Difference = @(Compare-Object -ReferenceObject $ExpectedSorted -DifferenceObject $ActualSorted) + if ($Difference.Count -gt 0 -or $Actual.Count -ne $Expected.Count) { + throw "$Label does not match the required set. Expected '$($Expected -join ', ')'; actual '$($Actual -join ', ')'." + } +} + +function Assert-ExactPropertySet { + param( + [Parameter(Mandatory)][object]$InputObject, + [Parameter(Mandatory)][string[]]$Expected, + [Parameter(Mandatory)][string]$Label + ) + + Assert-ExactStringSet -Actual @($InputObject.PSObject.Properties.Name) -Expected $Expected -Label "$Label properties" +} + +foreach ($RequiredPath in @($EvidencePath, $TestMatrixPath, $DependencyPolicyPath)) { + if (-not (Test-Path -LiteralPath $RequiredPath -PathType Leaf)) { + throw "Required authenticated-evidence input was not found: $RequiredPath" + } +} +$Evidence = Get-Content -LiteralPath $EvidencePath -Raw | ConvertFrom-Json -ErrorAction Stop +$TestMatrix = Get-Content -LiteralPath $TestMatrixPath -Raw | ConvertFrom-Json -ErrorAction Stop +$Policy = Get-Content -LiteralPath $DependencyPolicyPath -Raw | ConvertFrom-Json -ErrorAction Stop + +Assert-ExactPropertySet -InputObject $Evidence -Expected @('schemaVersion', 'evidenceType', 'contentFingerprint', 'provenance', 'acceptance', 'content') -Label 'Evidence envelope' +Assert-ExactPropertySet -InputObject $Evidence.provenance -Expected @('sourceCommitSha', 'captureStartedAtUtc', 'captureCompletedAtUtc') -Label 'Evidence provenance' +Assert-ExactPropertySet -InputObject $Evidence.acceptance -Expected @('status', 'acceptedAtUtc', 'acceptedBy', 'confidence') -Label 'Evidence acceptance' +Assert-ExactPropertySet -InputObject $Evidence.content -Expected @('bridge', 'bundleSourceFingerprint', 'credentialMode', 'credentialMaterialCaptured', 'authorizationBoundaryValidated', 'platformScope', 'writesPerformed', 'profiles') -Label 'Evidence content' +Assert-ExactPropertySet -InputObject $Evidence.content.bridge -Expected @('id', 'allowedReleaseVersion', 'expiresAtUtc') -Label 'Evidence bridge' + +if ([int]$Evidence.schemaVersion -ne 1 -or + [string]$Evidence.evidenceType -ne 'manual-interactive-transition' -or + -not $Evidence.content) { + throw 'Manual authenticated evidence has an unsupported schema, type, or missing content.' +} +$RecomputedContentFingerprint = Get-DLLPickleNormalizedEvidenceFingerprint -Evidence $Evidence +if ([string]$Evidence.contentFingerprint -ne $RecomputedContentFingerprint) { + throw "Manual authenticated evidence does not recompute to '$($Evidence.contentFingerprint)'." +} + +$Bridge = $Evidence.content.bridge +if ([string]$Bridge.id -ne 'initial-powershell-7.4-7.6-multitargeting-major' -or + [string]$Bridge.allowedReleaseVersion -ne '3.0.0') { + throw 'Manual authenticated evidence is not scoped exclusively to the initial 3.0.0 multi-target release.' +} +$CaptureStartedAtUtc = ConvertTo-DLLPickleUtcDateTimeOffset -Value $Evidence.provenance.captureStartedAtUtc +$CapturedAtUtc = ConvertTo-DLLPickleUtcDateTimeOffset -Value $Evidence.provenance.captureCompletedAtUtc +$ExpiresAtUtc = ConvertTo-DLLPickleUtcDateTimeOffset -Value $Bridge.expiresAtUtc +if ([string]$Evidence.provenance.sourceCommitSha -notmatch '^[a-f0-9]{40}$' -or + $CaptureStartedAtUtc -gt $CapturedAtUtc -or + $CapturedAtUtc -gt $NowUtc.ToUniversalTime()) { + throw 'Manual authenticated-evidence provenance has an invalid commit or capture window.' +} +if ($ExpiresAtUtc -ne $CaptureStartedAtUtc.AddDays(14) -or $CapturedAtUtc -ge $ExpiresAtUtc) { + throw 'The manual authenticated-evidence bridge must expire exactly 14 days after capture starts, after capture completes.' +} +if ($NowUtc.ToUniversalTime() -ge $ExpiresAtUtc) { + throw "Manual authenticated evidence expired at $($ExpiresAtUtc.ToString('o'))." +} +if ($Mode -eq 'Release') { + if ([string]$Evidence.acceptance.status -ne 'accepted' -or + [string]$Evidence.acceptance.acceptedBy -notmatch '^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$' -or + [string]$Evidence.acceptance.confidence -notin @('low', 'medium', 'high')) { + throw 'Manual authenticated evidence has not been explicitly accepted by a maintainer with a confidence level.' + } + $AcceptedAtUtc = ConvertTo-DLLPickleUtcDateTimeOffset -Value $Evidence.acceptance.acceptedAtUtc + if ($AcceptedAtUtc -lt $CapturedAtUtc -or + $AcceptedAtUtc -ge $ExpiresAtUtc -or + $AcceptedAtUtc -gt $NowUtc.ToUniversalTime()) { + throw "Manual authenticated evidence acceptance '$($AcceptedAtUtc.ToString('o'))' is outside its capture '$($CapturedAtUtc.ToString('o'))' to expiry '$($ExpiresAtUtc.ToString('o'))' window." + } +} + +if ([string]$Evidence.content.credentialMode -ne 'delegated-interactive' -or + $Evidence.content.credentialMaterialCaptured -ne $false -or + $Evidence.content.authorizationBoundaryValidated -ne $false -or + [string]$Evidence.content.platformScope -ne 'windows-x64-only' -or + $Evidence.content.writesPerformed -ne $false) { + throw 'Manual transition evidence must remain delegated-interactive, credential-free, Windows-only, zero-write, and explicitly not least-privilege proof.' +} + +$BundleFingerprintTool = Join-Path $PSScriptRoot 'Get-DLLPickleBundleSourceFingerprint.ps1' +$CurrentBundle = & $BundleFingerprintTool -RepositoryRoot $RepositoryRoot +if ([string]$Evidence.content.bundleSourceFingerprint -ne [string]$CurrentBundle.fingerprint) { + throw "Manual authenticated evidence is bound to bundle '$($Evidence.content.bundleSourceFingerprint)', but the current bundle is '$($CurrentBundle.fingerprint)'." +} + +$ExpectedProfiles = @( + foreach ($RuntimeProfile in @($TestMatrix.profiles)) { + [pscustomobject]@{ + ProfileKey = 'ps{0}.{1}-{2}-windows-x64' -f $RuntimeProfile.powerShellMajor, $RuntimeProfile.powerShellMinor, $RuntimeProfile.targetFramework + PowerShellVersion = [string]$RuntimeProfile.powerShellVersion + PowerShellLine = '{0}.{1}' -f $RuntimeProfile.powerShellMajor, $RuntimeProfile.powerShellMinor + DotNetVersion = [string]$RuntimeProfile.dotnetRuntimeVersion + DotNetMajor = [int]$RuntimeProfile.dotnetMajor + TargetFramework = [string]$RuntimeProfile.targetFramework + } + } +) +Assert-ExactStringSet -Actual @($Evidence.content.profiles.profileKey) -Expected @($ExpectedProfiles.ProfileKey) -Label 'Authenticated profile coverage' + +$ExpectedModuleNames = @(Get-DLLPickleOrdinalSequence -InputObject @($Policy.monitoredModules.name)) +$ExpectedScenarioIds = @( + 'graph-module-only', 'graph-dllpickle-first', 'graph-module-first', + 'exo-module-only', 'exo-dllpickle-first', 'exo-module-first', + 'az-module-only', 'az-dllpickle-first', 'az-module-first', + 'teams-module-only', 'teams-dllpickle-first', 'teams-module-first', + 'cross-import-order-1', 'cross-import-order-2' +) +$ProviderProbes = [ordered]@{ + graph = @('graph-context', 'graph-me-read') + exo = @('exo-mailbox-read') + az = @('az-context', 'az-resource-read', 'az-storage-account-read') + teams = @('teams-tenant-read') + cross = @('graph-context', 'graph-me-read', 'exo-mailbox-read', 'az-context', 'teams-tenant-read') +} +$ProviderAudiences = [ordered]@{ + graph = @('https://graph.microsoft.com') + exo = @('https://outlook.office365.com') + az = @('https://management.azure.com') + teams = @('https://api.spaces.skype.com') + cross = @('https://api.spaces.skype.com', 'https://graph.microsoft.com', 'https://management.azure.com', 'https://outlook.office365.com') +} +$ProviderModuleOrders = [ordered]@{ + graph = @('Microsoft.Graph.Authentication') + exo = @('ExchangeOnlineManagement') + az = @('Az.Accounts', 'Az.Resources', 'Az.Storage') + teams = @('MicrosoftTeams') +} + +foreach ($ExpectedProfile in $ExpectedProfiles) { + $ProfileRows = @($Evidence.content.profiles | Where-Object profileKey -eq $ExpectedProfile.ProfileKey) + if ($ProfileRows.Count -ne 1) { + throw "Expected exactly one authenticated profile '$($ExpectedProfile.ProfileKey)'." + } + $EvidenceProfile = $ProfileRows[0] + Assert-ExactPropertySet -InputObject $EvidenceProfile -Expected @('profileKey', 'powerShellVersion', 'powerShellLine', 'dotNetVersion', 'dotNetMajor', 'targetFramework', 'platform', 'architecture', 'runtimeExecutable', 'psHome', 'writesPerformed', 'inventoryFingerprint', 'moduleVersions', 'scenarios') -Label "Authenticated profile '$($ExpectedProfile.ProfileKey)'" + if ([string]$EvidenceProfile.powerShellVersion -ne $ExpectedProfile.PowerShellVersion -or + [string]$EvidenceProfile.powerShellLine -ne $ExpectedProfile.PowerShellLine -or + [string]$EvidenceProfile.dotNetVersion -ne $ExpectedProfile.DotNetVersion -or + [int]$EvidenceProfile.dotNetMajor -ne $ExpectedProfile.DotNetMajor -or + [string]$EvidenceProfile.targetFramework -ne $ExpectedProfile.TargetFramework -or + [string]$EvidenceProfile.platform -ne 'windows' -or + [string]$EvidenceProfile.architecture -ne 'x64' -or + $EvidenceProfile.writesPerformed -ne $false) { + throw "Authenticated profile '$($ExpectedProfile.ProfileKey)' does not match the exact zero-write Windows runtime contract." + } + if ([string]$EvidenceProfile.runtimeExecutable -notmatch '^runtime:' -or [string]$EvidenceProfile.psHome -notmatch '^runtime:') { + throw "Authenticated profile '$($ExpectedProfile.ProfileKey)' contains an unnormalized runtime path." + } + if ([string]$EvidenceProfile.inventoryFingerprint -notmatch '^[a-f0-9]{64}$') { + throw "Authenticated profile '$($ExpectedProfile.ProfileKey)' has no valid prepared module-inventory fingerprint." + } + Assert-ExactStringSet -Actual @($EvidenceProfile.moduleVersions.name) -Expected $ExpectedModuleNames -Label "Module versions for '$($ExpectedProfile.ProfileKey)'" + foreach ($ModuleVersion in @($EvidenceProfile.moduleVersions)) { + Assert-ExactPropertySet -InputObject $ModuleVersion -Expected @('name', 'version', 'manifest') -Label "Module-version row for '$($ExpectedProfile.ProfileKey)'" + if ([string]::IsNullOrWhiteSpace([string]$ModuleVersion.version) -or + [string]$ModuleVersion.manifest -notmatch '^upstream:') { + throw "Authenticated profile '$($ExpectedProfile.ProfileKey)' has an incomplete or unnormalized module-version row." + } + } + Assert-ExactStringSet -Actual @($EvidenceProfile.scenarios.scenarioId) -Expected $ExpectedScenarioIds -Label "Scenario coverage for '$($ExpectedProfile.ProfileKey)'" + + $ProfilePolicy = @($Policy.runtimeProfiles | Where-Object { + $_.powerShellLine -eq $ExpectedProfile.PowerShellLine -and $_.targetFramework -eq $ExpectedProfile.TargetFramework + }) + if ($ProfilePolicy.Count -ne 1) { + throw "No unique dependency policy exists for '$($ExpectedProfile.ProfileKey)'." + } + foreach ($Scenario in @($EvidenceProfile.scenarios)) { + Assert-ExactPropertySet -InputObject $Scenario -Expected @('scenarioId', 'profileKey', 'powerShellVersion', 'targetFramework', 'platform', 'architecture', 'inventoryFingerprint', 'importOrder', 'dllPickleTiming', 'expectedTokenAudiences', 'status', 'writesPerformed', 'probes', 'snapshots', 'errorType') -Label "Authenticated scenario '$($Scenario.scenarioId)'" + if ([string]$Scenario.profileKey -ne $ExpectedProfile.ProfileKey -or + [string]$Scenario.powerShellVersion -ne $ExpectedProfile.PowerShellVersion -or + [string]$Scenario.targetFramework -ne $ExpectedProfile.TargetFramework -or + [string]$Scenario.platform -ne 'windows' -or + [string]$Scenario.architecture -ne 'x64' -or + [string]$Scenario.inventoryFingerprint -ne [string]$EvidenceProfile.inventoryFingerprint) { + throw "Authenticated scenario '$($Scenario.scenarioId)' is not bound to exact profile '$($ExpectedProfile.ProfileKey)'." + } + if ([string]$Scenario.status -ne 'passed' -or $Scenario.writesPerformed -ne $false -or + -not [string]::IsNullOrWhiteSpace([string]$Scenario.errorType)) { + throw "Authenticated scenario '$($Scenario.scenarioId)' for '$($ExpectedProfile.ProfileKey)' did not pass zero-write validation." + } + $Provider = if ([string]$Scenario.scenarioId -like 'cross-*') { 'cross' } else { ([string]$Scenario.scenarioId -split '-')[0] } + $ExpectedTiming = if ($Provider -eq 'cross' -or [string]$Scenario.scenarioId -like '*-dllpickle-first') { + 'dllpickle-first' + } elseif ([string]$Scenario.scenarioId -like '*-module-first') { + 'module-first' + } else { + 'module-only' + } + if ([string]$Scenario.dllPickleTiming -ne $ExpectedTiming) { + throw "Authenticated scenario '$($Scenario.scenarioId)' has DLLPickle timing '$($Scenario.dllPickleTiming)', expected '$ExpectedTiming'." + } + if ($Provider -ne 'cross' -and (@($Scenario.importOrder) -join '|') -ne (@($ProviderModuleOrders[$Provider]) -join '|')) { + throw "Authenticated scenario '$($Scenario.scenarioId)' does not use the fixed provider module order." + } + Assert-ExactStringSet -Actual @($Scenario.probes.probeId) -Expected @($ProviderProbes[$Provider]) -Label "Probe coverage for '$($Scenario.scenarioId)'" + Assert-ExactStringSet -Actual @($Scenario.expectedTokenAudiences) -Expected @($ProviderAudiences[$Provider]) -Label "Declared token audiences for '$($Scenario.scenarioId)'" + foreach ($Probe in @($Scenario.probes)) { + Assert-ExactPropertySet -InputObject $Probe -Expected @('probeId', 'executed', 'status', 'durationMilliseconds', 'writesPerformed', 'errorType') -Label "Authenticated probe '$($Probe.probeId)'" + if ($Probe.executed -ne $true -or [string]$Probe.status -ne 'passed' -or $Probe.writesPerformed -ne $false -or + -not [string]::IsNullOrWhiteSpace([string]$Probe.errorType) -or [long]$Probe.durationMilliseconds -lt 0) { + throw "Authenticated probe '$($Probe.probeId)' in '$($Scenario.scenarioId)' is not an executed, passing, zero-write result." + } + } + Assert-ExactStringSet -Actual @($Scenario.snapshots.stage) -Expected @('before-authentication', 'after-connection', 'after-read-probe') -Label "ALC snapshots for '$($Scenario.scenarioId)'" + foreach ($Snapshot in @($Scenario.snapshots)) { + Assert-ExactPropertySet -InputObject $Snapshot -Expected @('stage', 'assemblies') -Label "ALC snapshot '$($Scenario.scenarioId)/$($Snapshot.stage)'" + foreach ($Assembly in @($Snapshot.assemblies)) { + Assert-ExactPropertySet -InputObject $Assembly -Expected @('name', 'version', 'sha256', 'selectedAsset', 'assemblyLoadContext', 'isCollectible') -Label "Assembly row in '$($Scenario.scenarioId)/$($Snapshot.stage)'" + if ([string]$Assembly.sha256 -notmatch '^[a-f0-9]{64}$' -or + [string]$Assembly.selectedAsset -notmatch '^(upstream|dllpickle|runtime):' -or + [string]$Assembly.name -notin @($Policy.trackedAssemblies) -or + [string]::IsNullOrWhiteSpace([string]$Assembly.name) -or + [string]::IsNullOrWhiteSpace([string]$Assembly.version) -or + [string]::IsNullOrWhiteSpace([string]$Assembly.assemblyLoadContext) -or + $Assembly.isCollectible -isnot [bool]) { + throw "ALC snapshot '$($Scenario.scenarioId)/$($Snapshot.stage)' contains an incomplete or unnormalized assembly row." + } + } + } + } + + $CrossScenarios = @(Get-DLLPickleOrdinalSequence -InputObject @($EvidenceProfile.scenarios | Where-Object scenarioId -like 'cross-*') -KeySelector { param($Scenario) [string]$Scenario.scenarioId }) + for ($OrderIndex = 0; $OrderIndex -lt 2; $OrderIndex++) { + $ExpectedOrder = @($ProfilePolicy[0].importOrders[$OrderIndex]) + if ((@($CrossScenarios[$OrderIndex].importOrder) -join '|') -ne ($ExpectedOrder -join '|')) { + throw "Authenticated cross-import scenario $($OrderIndex + 1) for '$($ExpectedProfile.ProfileKey)' does not match dependency policy." + } + } +} + +[pscustomobject]@{ + Mode = $Mode + EvidenceType = [string]$Evidence.evidenceType + EvidenceFingerprint = $RecomputedContentFingerprint + BundleSourceFingerprint = [string]$CurrentBundle.fingerprint + AllowedReleaseVersion = [string]$Bridge.allowedReleaseVersion + ExpiresAtUtc = $ExpiresAtUtc.ToString('o') + ProfileKeys = @($ExpectedProfiles.ProfileKey) + WritesPerformed = $false +} diff --git a/tools/Test-DLLPicklePackageArtifact.ps1 b/tools/Test-DLLPicklePackageArtifact.ps1 new file mode 100644 index 00000000..f83c1782 --- /dev/null +++ b/tools/Test-DLLPicklePackageArtifact.ps1 @@ -0,0 +1,237 @@ +<# +.SYNOPSIS + Verifies the supported-profile composition of the built DLLPickle module. + +.DESCRIPTION + Asserts that the module contains exactly the target-framework directories declared + by the shipped runtime policy, that each directory matches the build output used by + the packaging task, and that optional multi-pwsh test tooling is absent from the + artifact and runtime/build dependency declarations. + +.PARAMETER ModulePath + Built module directory to inspect. + +.PARAMETER BuildOutputRoot + Root containing one Release build-output directory per target framework. + +.PARAMETER Strict + Throw when any composition finding is present. +#> + +[CmdletBinding()] +param( + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$ModulePath = (Join-Path (Split-Path -Parent $PSScriptRoot) 'module/DLLPickle'), + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$BuildOutputRoot = (Join-Path (Split-Path -Parent $PSScriptRoot) 'src/DLLPickle.Build/bin/Release'), + + [Parameter()] + [switch]$SkipBuildOutputComparison, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$SupportPolicyPath = (Join-Path (Split-Path -Parent $PSScriptRoot) 'src/DLLPickle/SupportedRuntimeProfiles.json'), + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$ProjectPath = (Join-Path (Split-Path -Parent $PSScriptRoot) 'src/DLLPickle.Build/DLLPickle.csproj'), + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$LockFilePath = (Join-Path (Split-Path -Parent $PSScriptRoot) 'src/DLLPickle.Build/packages.lock.json'), + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$OutputPath = (Join-Path (Split-Path -Parent $PSScriptRoot) 'artifacts/package/artifact-composition.json'), + + [Parameter()] + [switch]$Strict +) + +$ErrorActionPreference = 'Stop' +$Findings = [System.Collections.Generic.List[object]]::new() + +function Add-DLLPickleArtifactFinding { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string]$Code, + + [Parameter(Mandatory)] + [string]$Message, + + [Parameter()] + [string]$TargetFramework + ) + + $Findings.Add([PSCustomObject]@{ + Code = $Code + TargetFramework = $TargetFramework + Message = $Message + }) +} + +function Get-DLLPickleRelativeFileSet { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string]$Root, + + [Parameter(Mandatory)] + [AllowEmptyCollection()] + [System.IO.FileInfo[]]$Files + ) + + @( + foreach ($File in $Files) { + [System.IO.Path]::GetRelativePath($Root, $File.FullName).Replace('\', '/') + } + ) | Sort-Object -Unique +} + +$ModuleManifestPath = Join-Path $ModulePath 'DLLPickle.psd1' +$RequiredPaths = @($ModulePath, $SupportPolicyPath, $ProjectPath, $LockFilePath, $ModuleManifestPath) +if (-not $SkipBuildOutputComparison.IsPresent) { + $RequiredPaths += $BuildOutputRoot +} +foreach ($RequiredPath in $RequiredPaths) { + if (-not (Test-Path -LiteralPath $RequiredPath)) { + throw "Required package-inspection path was not found: $RequiredPath" + } +} + +$ResolvedModulePath = (Resolve-Path -LiteralPath $ModulePath).Path +$ResolvedBuildOutputRoot = if ($SkipBuildOutputComparison.IsPresent) { $null } else { (Resolve-Path -LiteralPath $BuildOutputRoot).Path } +$SupportPolicy = Get-Content -LiteralPath $SupportPolicyPath -Raw | ConvertFrom-Json -ErrorAction Stop +$ExpectedTargetFrameworks = @($SupportPolicy.profiles.targetFramework | ForEach-Object { [string]$_ } | Sort-Object -Unique) +if ($ExpectedTargetFrameworks.Count -eq 0) { + throw 'The shipped support policy does not declare any target frameworks.' +} + +$BinPath = Join-Path $ResolvedModulePath 'bin' +if (-not (Test-Path -LiteralPath $BinPath -PathType Container)) { + Add-DLLPickleArtifactFinding -Code 'MissingBinDirectory' -Message "Module bin directory was not found: $BinPath" + $ActualTargetFrameworks = @() +} else { + $ActualTargetFrameworks = @(Get-ChildItem -LiteralPath $BinPath -Directory | Select-Object -ExpandProperty Name | Sort-Object -Unique) +} + +foreach ($TargetFramework in $ExpectedTargetFrameworks) { + if ($TargetFramework -notin $ActualTargetFrameworks) { + Add-DLLPickleArtifactFinding -Code 'MissingTargetFramework' -TargetFramework $TargetFramework -Message "Expected target-framework directory '$TargetFramework' is absent from the module artifact." + } +} +foreach ($TargetFramework in $ActualTargetFrameworks) { + if ($TargetFramework -notin $ExpectedTargetFrameworks) { + Add-DLLPickleArtifactFinding -Code 'UnexpectedTargetFramework' -TargetFramework $TargetFramework -Message "Unexpected target-framework directory '$TargetFramework' is present in the module artifact." + } +} + +$ProfileResults = @( + foreach ($TargetFramework in $ExpectedTargetFrameworks) { + $ArtifactTfmPath = Join-Path $BinPath $TargetFramework + $BuildTfmPath = if ($ResolvedBuildOutputRoot) { Join-Path $ResolvedBuildOutputRoot $TargetFramework } else { $null } + if (-not (Test-Path -LiteralPath $ArtifactTfmPath -PathType Container) -or ($BuildTfmPath -and -not (Test-Path -LiteralPath $BuildTfmPath -PathType Container))) { + if ($BuildTfmPath -and -not (Test-Path -LiteralPath $BuildTfmPath -PathType Container)) { + Add-DLLPickleArtifactFinding -Code 'MissingBuildOutput' -TargetFramework $TargetFramework -Message "Build output for '$TargetFramework' was not found at '$BuildTfmPath'." + } + continue + } + + $ActualDlls = @(Get-ChildItem -LiteralPath $ArtifactTfmPath -File -Filter '*.dll' | Select-Object -ExpandProperty Name | Sort-Object -Unique) + if ($ActualDlls.Count -eq 0) { + Add-DLLPickleArtifactFinding -Code 'EmptyTargetFramework' -TargetFramework $TargetFramework -Message "Target-framework directory '$TargetFramework' contains no managed assemblies." + } + if (-not $SkipBuildOutputComparison.IsPresent) { + $ExpectedDlls = @(Get-ChildItem -LiteralPath $BuildTfmPath -File -Filter '*.dll' | Where-Object Name -Match '^(Azure\.|Microsoft\.|System\.)' | Select-Object -ExpandProperty Name | Sort-Object -Unique) + $ActualComparedDlls = @($ActualDlls | Where-Object { $_ -match '^(Azure\.|Microsoft\.|System\.)' }) + foreach ($Name in @($ExpectedDlls | Where-Object { $_ -notin $ActualComparedDlls })) { + Add-DLLPickleArtifactFinding -Code 'MissingManagedAsset' -TargetFramework $TargetFramework -Message "Expected managed asset '$Name' is absent." + } + foreach ($Name in @($ActualComparedDlls | Where-Object { $_ -notin $ExpectedDlls })) { + Add-DLLPickleArtifactFinding -Code 'UnexpectedManagedAsset' -TargetFramework $TargetFramework -Message "Managed asset '$Name' is not present in the packaging build output." + } + } + + $BuildRuntimePath = if ($BuildTfmPath) { Join-Path $BuildTfmPath 'runtimes' } else { $null } + $ArtifactRuntimePath = Join-Path $ArtifactTfmPath 'runtimes' + $ExpectedNativeFiles = if ($BuildRuntimePath -and (Test-Path -LiteralPath $BuildRuntimePath -PathType Container)) { + Get-DLLPickleRelativeFileSet -Root $BuildRuntimePath -Files @(Get-ChildItem -LiteralPath $BuildRuntimePath -File -Recurse | Where-Object FullName -Match '[\\/]native[\\/]') + } else { + @() + } + $ActualNativeFiles = if (Test-Path -LiteralPath $ArtifactRuntimePath -PathType Container) { + Get-DLLPickleRelativeFileSet -Root $ArtifactRuntimePath -Files @( + Get-ChildItem -LiteralPath $ArtifactRuntimePath -File -Recurse | + Where-Object FullName -Match '[\\/]native[\\/]' + ) + } else { + @() + } + if (-not $SkipBuildOutputComparison.IsPresent) { + foreach ($Name in @($ExpectedNativeFiles | Where-Object { $_ -notin $ActualNativeFiles })) { + Add-DLLPickleArtifactFinding -Code 'MissingNativeAsset' -TargetFramework $TargetFramework -Message "Expected native asset '$Name' is absent." + } + foreach ($Name in @($ActualNativeFiles | Where-Object { $_ -notin $ExpectedNativeFiles })) { + Add-DLLPickleArtifactFinding -Code 'UnexpectedNativeAsset' -TargetFramework $TargetFramework -Message "Native asset '$Name' is not present in the packaging build output." + } + } + + [PSCustomObject]@{ + TargetFramework = $TargetFramework + ManagedAssets = @($ActualDlls) + NativeAssets = @($ActualNativeFiles) + } + } +) + +$ForbiddenPattern = '(?i)multi[-_ ]?pwsh' +$ForbiddenHits = [System.Collections.Generic.List[object]]::new() +$ArtifactFiles = @(Get-ChildItem -LiteralPath $ResolvedModulePath -File -Recurse) +foreach ($File in $ArtifactFiles) { + $RelativePath = [System.IO.Path]::GetRelativePath($ResolvedModulePath, $File.FullName).Replace('\', '/') + if ($RelativePath -match $ForbiddenPattern) { + $ForbiddenHits.Add([PSCustomObject]@{ Source = 'ArtifactPath'; Path = $RelativePath }) + } + + if ($File.Extension -in @('.ps1', '.psm1', '.psd1', '.ps1xml', '.json', '.xml', '.txt', '.md', '.config')) { + if ((Get-Content -LiteralPath $File.FullName -Raw -ErrorAction Stop) -match $ForbiddenPattern) { + $ForbiddenHits.Add([PSCustomObject]@{ Source = 'ArtifactContent'; Path = $RelativePath }) + } + } +} +foreach ($DeclarationPath in @($ProjectPath, $LockFilePath, $ModuleManifestPath)) { + if ((Get-Content -LiteralPath $DeclarationPath -Raw -ErrorAction Stop) -match $ForbiddenPattern) { + $ForbiddenHits.Add([PSCustomObject]@{ Source = 'DependencyDeclaration'; Path = $DeclarationPath }) + } +} +foreach ($Hit in $ForbiddenHits) { + Add-DLLPickleArtifactFinding -Code 'ForbiddenMultiPwshReference' -Message "Forbidden multi-pwsh reference detected in $($Hit.Source): $($Hit.Path)" +} + +$Report = [PSCustomObject]@{ + SchemaVersion = 1 + GeneratedAtUtc = [System.DateTimeOffset]::UtcNow.ToString('o') + ModulePath = $ResolvedModulePath + ExpectedTargetFrameworks = @($ExpectedTargetFrameworks) + ActualTargetFrameworks = @($ActualTargetFrameworks) + Profiles = @($ProfileResults) + ForbiddenHits = @($ForbiddenHits) + Findings = @($Findings) + Passed = $Findings.Count -eq 0 +} + +$OutputDirectory = Split-Path -Path $OutputPath -Parent +if ($OutputDirectory -and -not (Test-Path -LiteralPath $OutputDirectory -PathType Container)) { + $null = New-Item -Path $OutputDirectory -ItemType Directory -Force +} +$Report | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $OutputPath -Encoding UTF8 + +if ($Strict.IsPresent -and -not $Report.Passed) { + throw "DLLPickle artifact inspection failed: $($Findings.Message -join ' ')" +} + +$Report diff --git a/tools/Test-DLLPicklePackageReferenceUpdate.ps1 b/tools/Test-DLLPicklePackageReferenceUpdate.ps1 new file mode 100644 index 00000000..be5abe94 --- /dev/null +++ b/tools/Test-DLLPicklePackageReferenceUpdate.ps1 @@ -0,0 +1,127 @@ +<# +.SYNOPSIS +Validates that a project-file update changes only existing package versions. + +.DESCRIPTION +Compares trusted base and candidate project files after masking Version attribute +values on existing PackageReference elements. Package additions, removals, +conditions, metadata, properties, comments, and all other project content must +remain unchanged. At least one package version must change, and every new value +must use a numeric NuGet version or floating-version form. + +.PARAMETER BaseProjectPath +Path to the trusted base-branch project file. + +.PARAMETER CandidateProjectPath +Path to the candidate project file. + +.OUTPUTS +System.Management.Automation.PSCustomObject +#> + +[CmdletBinding()] +[OutputType([pscustomobject])] +param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$BaseProjectPath, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$CandidateProjectPath +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +function Get-DLLPickleProjectSnapshot { + [CmdletBinding()] + [OutputType([pscustomobject])] + param ( + [Parameter(Mandatory)] + [string]$Path + ) + + if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { + throw "Project file was not found: $Path" + } + + try { + $Document = [System.Xml.Linq.XDocument]::Parse( + (Get-Content -LiteralPath $Path -Raw), + [System.Xml.Linq.LoadOptions]::PreserveWhitespace + ) + } catch { + throw "Project file '$Path' is not valid XML: $($_.Exception.Message)" + } + + $PackageVersions = [ordered]@{} + $PackageReferences = @($Document.Descendants() | Where-Object { $_.Name.LocalName -eq 'PackageReference' }) + if ($PackageReferences.Count -eq 0) { + throw "Project file '$Path' contains no PackageReference elements." + } + + foreach ($PackageReference in $PackageReferences) { + $IdentityAttributes = @($PackageReference.Attributes() | Where-Object { $_.Name.LocalName -in @('Include', 'Update') }) + $VersionAttributes = @($PackageReference.Attributes() | Where-Object { $_.Name.LocalName -eq 'Version' }) + if ($IdentityAttributes.Count -ne 1 -or $VersionAttributes.Count -ne 1) { + throw "Every PackageReference in '$Path' must have exactly one Include or Update attribute and one Version attribute." + } + + $Identity = '{0}:{1}' -f $IdentityAttributes[0].Name.LocalName, $IdentityAttributes[0].Value + if ($PackageVersions.Contains($Identity)) { + throw "Project file '$Path' contains duplicate PackageReference identity '$Identity'." + } + if ([string]::IsNullOrWhiteSpace($VersionAttributes[0].Value)) { + throw "PackageReference '$Identity' in '$Path' has no version value." + } + + $PackageVersions[$Identity] = $VersionAttributes[0].Value + $VersionAttributes[0].Value = '__DLLPICKLE_ALLOWED_VERSION__' + } + + [pscustomobject]@{ + NormalizedProject = $Document.ToString([System.Xml.Linq.SaveOptions]::DisableFormatting) + PackageVersions = $PackageVersions + } +} + +$Base = Get-DLLPickleProjectSnapshot -Path $BaseProjectPath +$Candidate = Get-DLLPickleProjectSnapshot -Path $CandidateProjectPath +$BaseKeys = @($Base.PackageVersions.Keys) +$CandidateKeys = @($Candidate.PackageVersions.Keys) +if ($BaseKeys.Count -ne $CandidateKeys.Count -or + @($BaseKeys | Where-Object { -not $Candidate.PackageVersions.Contains($_) }).Count -gt 0) { + throw 'The candidate project adds, removes, or renames a PackageReference.' +} + +$NuGetVersionExpressionPattern = '^(?:' + + '\d+(?:\.\d+){0,3}(?:-[0-9A-Za-z](?:[0-9A-Za-z.-]*[0-9A-Za-z])?)?(?:\+[0-9A-Za-z](?:[0-9A-Za-z.-]*[0-9A-Za-z])?)?' + + '|\d+(?:\.\d+){0,2}\.\*(?:-\*)?' + + '|\d+(?:\.\d+){0,3}-(?:\*|[0-9A-Za-z](?:[0-9A-Za-z.-]*[0-9A-Za-z])?\.\*)' + + '|\*|\*-\*' + + ')$' +$ChangedPackages = @( + foreach ($PackageKey in $BaseKeys) { + $BaseVersion = [string]$Base.PackageVersions[$PackageKey] + $CandidateVersion = [string]$Candidate.PackageVersions[$PackageKey] + if ($BaseVersion -ne $CandidateVersion) { + if ($CandidateVersion -notmatch $NuGetVersionExpressionPattern) { + throw "PackageReference '$PackageKey' has unsupported candidate version '$CandidateVersion'." + } + $PackageKey + } + } +) +if (-not [string]::Equals($Base.NormalizedProject, $Candidate.NormalizedProject, [System.StringComparison]::Ordinal)) { + throw 'The candidate project changes content other than PackageReference Version attribute values.' +} + +if ($ChangedPackages.Count -eq 0) { + throw 'The candidate project does not change any PackageReference Version attribute.' +} + +[pscustomobject]@{ + IsVersionOnlyUpdate = $true + ChangedPackages = $ChangedPackages +} diff --git a/tools/Test-DLLPickleProfileConflictBaseline.ps1 b/tools/Test-DLLPickleProfileConflictBaseline.ps1 new file mode 100644 index 00000000..f1c0166a --- /dev/null +++ b/tools/Test-DLLPickleProfileConflictBaseline.ps1 @@ -0,0 +1,230 @@ +<# +.SYNOPSIS +Fail closed when a profile-specific upstream conflict baseline is absent or has drifted. + +.PARAMETER PolicyPath +Path to the profile-aware dependency policy. + +.PARAMETER ConflictMatrixPath +Path to a current profile-keyed conflict matrix. + +.PARAMETER ScenarioEvidencePath +Path to the deterministic two-order, with/without-DLLPickle scenario report for +the same exact profile. + +.PARAMETER NormalizedEvidencePath +Path to the durable normalized candidate snapshot generated from the same run. + +.PARAMETER PassThru +Return a structured comparison result after validation. + +.PARAMETER OutputPath +Optional path for the structured result. The result is written before a missing, +unaccepted, or drifted baseline causes the command to fail closed. + +.OUTPUTS +System.Management.Automation.PSCustomObject +#> + +[CmdletBinding()] +[OutputType([pscustomobject])] +param ( + [Parameter(Mandatory)] + [string]$PolicyPath, + + [Parameter(Mandatory)] + [string]$ConflictMatrixPath, + + [Parameter(Mandatory)] + [string]$ScenarioEvidencePath, + + [Parameter(Mandatory)] + [string]$NormalizedEvidencePath, + + [Parameter()] + [switch]$PassThru, + + [Parameter()] + [string]$OutputPath +) + +$ErrorActionPreference = 'Stop' +. (Join-Path $PSScriptRoot 'DLLPickle.ProfileEvidence.ps1') +$Policy = Get-Content -LiteralPath $PolicyPath -Raw -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop +$Matrix = Get-Content -LiteralPath $ConflictMatrixPath -Raw -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop +$ScenarioEvidence = Get-Content -LiteralPath $ScenarioEvidencePath -Raw -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop +$NormalizedEvidence = Get-Content -LiteralPath $NormalizedEvidencePath -Raw -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop + +$CandidateEvidenceFingerprint = Get-DLLPickleNormalizedEvidenceFingerprint -Evidence $NormalizedEvidence +if ([string]$NormalizedEvidence.contentFingerprint -ne $CandidateEvidenceFingerprint) { + throw "Normalized profile evidence content does not recompute to '$($NormalizedEvidence.contentFingerprint)'." +} +if (-not $Matrix.Profile -or [string]::IsNullOrWhiteSpace([string]$Matrix.ProfileKey)) { + throw 'The conflict matrix is not keyed to an exact runtime profile.' +} +$MatrixProfileKeyValues = @( + [string]$Matrix.Profile.PowerShellLine + [string]$Matrix.Profile.TargetFramework + [string]$Matrix.Profile.Platform + [string]$Matrix.Profile.Architecture +) +if (@($MatrixProfileKeyValues | Where-Object { [string]::IsNullOrWhiteSpace($_) }).Count -gt 0) { + throw "Conflict matrix '$($Matrix.ProfileKey)' lacks a complete PowerShell line, TFM, platform, and architecture profile." +} +$DerivedMatrixProfileKey = 'ps{0}-{1}-{2}-{3}' -f $MatrixProfileKeyValues +if ([string]$Matrix.ProfileKey -ne $DerivedMatrixProfileKey) { + throw "Conflict matrix profile key '$($Matrix.ProfileKey)' does not match derived profile key '$DerivedMatrixProfileKey'." +} +if (-not $ScenarioEvidence.Profile) { + throw "Scenario evidence for '$($Matrix.ProfileKey)' has no observed runtime profile." +} +$ScenarioProfileKeyValues = @( + [string]$ScenarioEvidence.Profile.PowerShellLine + [string]$ScenarioEvidence.Profile.TargetFramework + [string]$ScenarioEvidence.Profile.Platform + [string]$ScenarioEvidence.Profile.Architecture +) +if (@($ScenarioProfileKeyValues | Where-Object { [string]::IsNullOrWhiteSpace($_) }).Count -gt 0) { + throw "Scenario evidence '$($ScenarioEvidence.ProfileKey)' lacks a complete PowerShell line, TFM, platform, and architecture profile." +} +$DerivedScenarioProfileKey = 'ps{0}-{1}-{2}-{3}' -f $ScenarioProfileKeyValues +if ([string]$ScenarioEvidence.ProfileKey -ne $DerivedScenarioProfileKey) { + throw "Scenario evidence profile key '$($ScenarioEvidence.ProfileKey)' does not match derived profile key '$DerivedScenarioProfileKey'." +} +if ($DerivedScenarioProfileKey -ne $DerivedMatrixProfileKey) { + throw "Scenario evidence profile '$DerivedScenarioProfileKey' does not match conflict profile '$DerivedMatrixProfileKey'." +} +$ObservedScenarioAssemblies = @( + foreach ($Scenario in @($ScenarioEvidence.Scenarios)) { + foreach ($Assembly in @($Scenario.Assemblies)) { + $Assembly + } + } +) +if ($ObservedScenarioAssemblies.Count -eq 0) { + throw "Scenario evidence for '$($Matrix.ProfileKey)' contains no observed tracked assemblies." +} +foreach ($ObservedAssembly in $ObservedScenarioAssemblies) { + if ([string]$ObservedAssembly.Platform -ne [string]$Matrix.Profile.Platform -or + [string]$ObservedAssembly.Architecture -ne [string]$Matrix.Profile.Architecture) { + throw "Scenario assembly '$($ObservedAssembly.Name)' was observed on '$($ObservedAssembly.Platform)/$($ObservedAssembly.Architecture)', expected '$($Matrix.Profile.Platform)/$($Matrix.Profile.Architecture)' for '$($Matrix.ProfileKey)'." + } +} +if (-not $ScenarioEvidence.Passed -or $ScenarioEvidence.WritesPerformed -or $ScenarioEvidence.ValidationTier -ne 'deterministic-import-no-auth') { + throw "Scenario evidence for '$($Matrix.ProfileKey)' is not a passing zero-write deterministic tier." +} +if ([string]$NormalizedEvidence.content.profile.profileKey -ne [string]$Matrix.ProfileKey) { + throw "Normalized evidence profile '$($NormalizedEvidence.content.profile.profileKey)' does not match '$($Matrix.ProfileKey)'." +} +if ([string]$NormalizedEvidence.content.validation.deterministicImportNoAuth.conflictSurfaceFingerprint -ne [string]$Matrix.Fingerprint -or + [string]$NormalizedEvidence.content.validation.deterministicImportNoAuth.scenarioFingerprint -ne [string]$ScenarioEvidence.ScenarioFingerprint) { + throw "Normalized evidence for '$($Matrix.ProfileKey)' does not bind the current conflict and scenario fingerprints." +} +if ([string]$NormalizedEvidence.content.validation.deterministicImportNoAuth.status -ne 'passed' -or + $NormalizedEvidence.content.validation.deterministicImportNoAuth.writesPerformed -or + $NormalizedEvidence.content.validation.authenticatedReadOnly.writesPerformed) { + throw "Normalized evidence for '$($Matrix.ProfileKey)' is not a passing zero-write deterministic snapshot." +} + +$ProfilePolicy = @($Policy.runtimeProfiles | Where-Object { + $_.powerShellLine -eq $Matrix.Profile.PowerShellLine -and + $_.targetFramework -eq $Matrix.Profile.TargetFramework + }) +if ($ProfilePolicy.Count -ne 1) { + throw "No unique dependency-policy profile matches '$($Matrix.ProfileKey)'." +} + +$Platform = [string]$Matrix.Profile.Platform +$BaselineProperty = $ProfilePolicy[0].baselines.PSObject.Properties | + Where-Object Name -eq $Platform | + Select-Object -First 1 +if (-not $BaselineProperty) { + throw "No $Platform conflict baseline is declared for '$($Matrix.ProfileKey)'." +} + +$Baseline = $BaselineProperty.Value +$BaselineFingerprint = [string]$Baseline.conflictSurfaceFingerprint +$CurrentFingerprint = [string]$Matrix.Fingerprint +$BaselineScenarioFingerprint = [string]$Baseline.scenarioFingerprint +$CurrentScenarioFingerprint = [string]$ScenarioEvidence.ScenarioFingerprint +$BaselineEvidencePath = [string]$Baseline.evidencePath +$BaselineEvidenceFingerprint = [string]$Baseline.evidenceFingerprint +$Status = if ( + $Baseline.status -ne 'accepted' -or + [string]::IsNullOrWhiteSpace($BaselineFingerprint) -or + [string]::IsNullOrWhiteSpace($BaselineScenarioFingerprint) -or + [string]::IsNullOrWhiteSpace($BaselineEvidencePath) -or + [string]::IsNullOrWhiteSpace($BaselineEvidenceFingerprint) +) { + 'RequiresAcceptance' +} elseif ( + $CurrentFingerprint -ne $BaselineFingerprint -or + $CurrentScenarioFingerprint -ne $BaselineScenarioFingerprint -or + $CandidateEvidenceFingerprint -ne $BaselineEvidenceFingerprint +) { + 'Drifted' +} else { + 'AcceptedUnchanged' +} + +$FailureDetail = $null +if ($Status -eq 'AcceptedUnchanged') { + try { + $ResolvedPolicyPath = (Resolve-Path -LiteralPath $PolicyPath).Path + $CommittedEvidencePath = Join-Path -Path (Split-Path -Path $ResolvedPolicyPath -Parent) -ChildPath $BaselineEvidencePath + if (-not (Test-Path -LiteralPath $CommittedEvidencePath -PathType Leaf)) { + throw "Accepted evidence for '$($Matrix.ProfileKey)' was not found at '$CommittedEvidencePath'." + } + $CommittedEvidence = Get-Content -LiteralPath $CommittedEvidencePath -Raw -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop + $CommittedEvidenceFingerprint = Get-DLLPickleNormalizedEvidenceFingerprint -Evidence $CommittedEvidence + if ([string]$CommittedEvidence.contentFingerprint -ne $CommittedEvidenceFingerprint -or + $CommittedEvidenceFingerprint -ne $BaselineEvidenceFingerprint) { + throw "Accepted evidence for '$($Matrix.ProfileKey)' does not recompute to the policy fingerprint '$BaselineEvidenceFingerprint'." + } + if ([string]$CommittedEvidence.content.profile.profileKey -ne [string]$Matrix.ProfileKey -or + [string]$CommittedEvidence.content.validation.deterministicImportNoAuth.conflictSurfaceFingerprint -ne $BaselineFingerprint -or + [string]$CommittedEvidence.content.validation.deterministicImportNoAuth.scenarioFingerprint -ne $BaselineScenarioFingerprint) { + throw "Accepted evidence for '$($Matrix.ProfileKey)' does not bind the policy profile, conflict, and scenario fingerprints." + } + } catch { + $Status = 'InvalidCommittedEvidence' + $FailureDetail = $_.Exception.Message + } +} + +$FindingCanonicalText = '{0}|{1}|conflict:{2}>{3}|scenario:{4}>{5}|evidence:{6}>{7}' -f $Matrix.ProfileKey, $Status, $BaselineFingerprint, $CurrentFingerprint, $BaselineScenarioFingerprint, $CurrentScenarioFingerprint, $BaselineEvidenceFingerprint, $CandidateEvidenceFingerprint +$FindingBytes = [System.Text.Encoding]::UTF8.GetBytes($FindingCanonicalText) +$FindingFingerprint = [System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::HashData($FindingBytes)).Replace('-', '').ToLowerInvariant() +$Result = [pscustomobject]@{ + ProfileKey = [string]$Matrix.ProfileKey + BaselineStatus = [string]$Baseline.status + BaselineFingerprint = $BaselineFingerprint + CurrentFingerprint = $CurrentFingerprint + BaselineScenarioFingerprint = $BaselineScenarioFingerprint + CurrentScenarioFingerprint = $CurrentScenarioFingerprint + BaselineEvidenceFingerprint = $BaselineEvidenceFingerprint + CurrentEvidenceFingerprint = $CandidateEvidenceFingerprint + BaselineEvidencePath = $BaselineEvidencePath + FindingFingerprint = $FindingFingerprint + Status = $Status + FailureDetail = $FailureDetail +} + +if (-not [string]::IsNullOrWhiteSpace($OutputPath)) { + $OutputDirectory = Split-Path -Path $OutputPath -Parent + if ($OutputDirectory -and -not (Test-Path -LiteralPath $OutputDirectory -PathType Container)) { + $null = New-Item -Path $OutputDirectory -ItemType Directory -Force + } + $Result | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $OutputPath -Encoding UTF8 +} + +if ($Status -eq 'RequiresAcceptance') { + throw "The conflict baseline for '$($Matrix.ProfileKey)' is not accepted. Current status: '$($Baseline.status)'. Review the profile evidence before release or merge." +} +if ($Status -eq 'Drifted') { + throw "Upstream profile evidence drift detected for '$($Matrix.ProfileKey)': baseline conflict '$BaselineFingerprint', current conflict '$CurrentFingerprint'; baseline scenario '$BaselineScenarioFingerprint', current scenario '$CurrentScenarioFingerprint'; baseline evidence '$BaselineEvidenceFingerprint', current evidence '$CandidateEvidenceFingerprint'." +} +if ($Status -eq 'InvalidCommittedEvidence') { + throw $FailureDetail +} +if ($PassThru) { $Result } diff --git a/tools/Test-DLLPickleRuntimeProfilePolicy.ps1 b/tools/Test-DLLPickleRuntimeProfilePolicy.ps1 new file mode 100644 index 00000000..f1a506a1 --- /dev/null +++ b/tools/Test-DLLPickleRuntimeProfilePolicy.ps1 @@ -0,0 +1,220 @@ +<# +.SYNOPSIS + Validates DLLPickle runtime-profile and lifecycle policy data. + +.DESCRIPTION + Confirms that shipped runtime mappings and non-shipped CI mappings agree, + then evaluates lifecycle expiration, retirement proximity, and evidence + freshness. Release mode fails closed on expired support or stale evidence. + +.PARAMETER RuntimePolicyPath + Path to the shipped runtime profile policy. + +.PARAMETER TestMatrixPath + Path to the non-shipped exact test matrix. + +.PARAMETER LifecycleEvidencePath + Optional current support-update discovery report. When supplied, the report + must describe a release-current support contract, and its generation time is + used instead of the committed matrix timestamp for evidence freshness. + +.PARAMETER Mode + Release fails closed. Scheduled emits warnings before retirement and for stale evidence. + +.PARAMETER AsOfUtc + UTC timestamp used for deterministic lifecycle evaluation. + +.PARAMETER PassThru + Returns one structured result per supported profile. + +.EXAMPLE + ./tools/Test-DLLPickleRuntimeProfilePolicy.ps1 -Mode Release + +.OUTPUTS + PSCustomObject when PassThru is specified. +#> + +[CmdletBinding()] +param( + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$RuntimePolicyPath = (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -ChildPath 'src/DLLPickle/SupportedRuntimeProfiles.json'), + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$TestMatrixPath = (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -ChildPath 'build/powershell-test-matrix.json'), + + [Parameter()] + [string]$LifecycleEvidencePath, + + [Parameter()] + [ValidateSet('Release', 'Scheduled')] + [string]$Mode = 'Release', + + [Parameter()] + [datetime]$AsOfUtc = [datetime]::UtcNow, + + [Parameter()] + [switch]$PassThru +) + +$ErrorActionPreference = 'Stop' + +foreach ($RequiredInput in @( + [PSCustomObject]@{ Name = 'Runtime profile policy'; Path = $RuntimePolicyPath } + [PSCustomObject]@{ Name = 'PowerShell test matrix'; Path = $TestMatrixPath } + )) { + if (-not (Test-Path -LiteralPath $RequiredInput.Path -PathType Leaf)) { + throw "$($RequiredInput.Name) file not found: $($RequiredInput.Path)" + } +} +if (-not [string]::IsNullOrWhiteSpace($LifecycleEvidencePath) -and -not (Test-Path -LiteralPath $LifecycleEvidencePath -PathType Leaf)) { + throw "PowerShell support-update evidence file not found: $LifecycleEvidencePath" +} + +$RuntimePolicy = Get-Content -LiteralPath $RuntimePolicyPath -Raw | ConvertFrom-Json -ErrorAction Stop +$TestMatrix = Get-Content -LiteralPath $TestMatrixPath -Raw | ConvertFrom-Json -ErrorAction Stop +if ($RuntimePolicy.schemaVersion -ne 1 -or $TestMatrix.schemaVersion -ne 1) { + throw 'Unsupported runtime-profile policy schema version.' +} + +$RuntimeProfiles = @($RuntimePolicy.profiles) +$TestProfiles = @($TestMatrix.profiles) +if ($RuntimeProfiles.Count -eq 0 -or $TestProfiles.Count -eq 0) { + throw 'Runtime-profile policy must contain at least one profile.' +} + +$RuntimeKeys = @($RuntimeProfiles | ForEach-Object { + '{0}.{1}|{2}|{3}' -f $_.powerShellMajor, $_.powerShellMinor, $_.dotnetMajor, $_.targetFramework + }) +$TestKeys = @($TestProfiles | ForEach-Object { + '{0}.{1}|{2}|{3}' -f $_.powerShellMajor, $_.powerShellMinor, $_.dotnetMajor, $_.targetFramework + }) +if (@($RuntimeKeys | Sort-Object -Unique).Count -ne $RuntimeKeys.Count) { + throw 'Shipped runtime-profile policy contains duplicate profiles.' +} +if (@($TestKeys | Sort-Object -Unique).Count -ne $TestKeys.Count) { + throw 'Test matrix contains duplicate profiles.' +} +if ((@($RuntimeKeys | Sort-Object) -join [char]0) -ne (@($TestKeys | Sort-Object) -join [char]0)) { + throw 'Shipped runtime-profile policy and CI test-matrix profile sets do not align.' +} + +$LifecycleEvidence = if (-not [string]::IsNullOrWhiteSpace($LifecycleEvidencePath)) { + Get-Content -LiteralPath $LifecycleEvidencePath -Raw | ConvertFrom-Json -ErrorAction Stop +} else { + $null +} +if ($LifecycleEvidence) { + $RequiredEvidenceProperties = @( + 'generatedAtUtc', + 'patchUpdates', + 'newLines', + 'lifecycle', + 'lifecycleDateChanges', + 'lifecycleMissingLines', + 'undeclaredSupportedLines', + 'supportContractReviewRequired' + ) + $MissingEvidenceProperties = @($RequiredEvidenceProperties | Where-Object { $LifecycleEvidence.PSObject.Properties.Name -notcontains $_ }) + if ($LifecycleEvidence.schemaVersion -ne 1 -or $MissingEvidenceProperties.Count -gt 0 -or [string]::IsNullOrWhiteSpace([string]$LifecycleEvidence.generatedAtUtc)) { + throw 'PowerShell support-update evidence has an unsupported schema or no generation timestamp.' + } + $LiveEvidenceViolations = [System.Collections.Generic.List[string]]::new() + if (@($LifecycleEvidence.patchUpdates).Count -gt 0) { $LiveEvidenceViolations.Add('newer servicing patches') } + if (@($LifecycleEvidence.newLines).Count -gt 0) { $LiveEvidenceViolations.Add('new GA PowerShell lines') } + if (@($LifecycleEvidence.lifecycleDateChanges).Count -gt 0) { $LiveEvidenceViolations.Add('lifecycle date changes') } + if (@($LifecycleEvidence.lifecycleMissingLines).Count -gt 0) { $LiveEvidenceViolations.Add('declared lifecycle lines missing from live evidence') } + if (@($LifecycleEvidence.undeclaredSupportedLines).Count -gt 0) { $LiveEvidenceViolations.Add('undeclared Microsoft-supported lines') } + if (@($LifecycleEvidence.lifecycle | Where-Object Status -EQ 'Expired').Count -gt 0) { $LiveEvidenceViolations.Add('expired PowerShell lines') } + $DeclaredReleaseLines = @($TestProfiles | ForEach-Object { '{0}.{1}' -f $_.powerShellMajor, $_.powerShellMinor } | Sort-Object -Unique) + $EvidenceReleaseLines = @($LifecycleEvidence.lifecycle.ReleaseLine | ForEach-Object { [string]$_ } | Sort-Object -Unique) + if (($DeclaredReleaseLines -join [char]0) -ne ($EvidenceReleaseLines -join [char]0)) { $LiveEvidenceViolations.Add('live lifecycle rows do not align with declared PowerShell lines') } + if ($LiveEvidenceViolations.Count -gt 0) { + throw "PowerShell support-update evidence is not release-current: $($LiveEvidenceViolations -join '; ')." + } +} + +$VerifiedTimestamp = if ($LifecycleEvidence) { [string]$LifecycleEvidence.generatedAtUtc } else { [string]$TestMatrix.lastVerifiedUtc } +$VerifiedUtc = [datetime]::Parse( + $VerifiedTimestamp, + [System.Globalization.CultureInfo]::InvariantCulture, + [System.Globalization.DateTimeStyles]::AssumeUniversal -bor [System.Globalization.DateTimeStyles]::AdjustToUniversal +) +$EvaluationUtc = $AsOfUtc.ToUniversalTime() +$EvidenceAgeDays = [math]::Max(0, ($EvaluationUtc - $VerifiedUtc).TotalDays) +$EvidenceIsStale = $EvidenceAgeDays -gt [double]$TestMatrix.evidenceFreshnessDays + +$Results = [System.Collections.Generic.List[object]]::new() +$ExpiredLines = [System.Collections.Generic.List[string]]::new() +$PacificTimeZone = try { + [System.TimeZoneInfo]::FindSystemTimeZoneById('America/Los_Angeles') +} catch { + [System.TimeZoneInfo]::FindSystemTimeZoneById('Pacific Standard Time') +} +foreach ($TestProfile in $TestProfiles) { + $Version = [version]$TestProfile.powerShellVersion + if ($Version.Major -ne $TestProfile.powerShellMajor -or $Version.Minor -ne $TestProfile.powerShellMinor) { + throw "Exact PowerShell patch '$($TestProfile.powerShellVersion)' does not match its declared release line." + } + if ($TestProfile.targetFramework -ne "net$($TestProfile.dotnetMajor).0") { + throw "Target framework '$($TestProfile.targetFramework)' does not match CLR major '$($TestProfile.dotnetMajor)'." + } + + $EndDate = [datetime]::ParseExact( + [string]$TestProfile.lifecycleEndDate, + 'yyyy-MM-dd', + [System.Globalization.CultureInfo]::InvariantCulture, + [System.Globalization.DateTimeStyles]::None + ) + $EndExclusivePacific = [datetime]::SpecifyKind($EndDate.AddDays(1), [System.DateTimeKind]::Unspecified) + $EndExclusiveUtc = [System.TimeZoneInfo]::ConvertTimeToUtc($EndExclusivePacific, $PacificTimeZone) + $DaysRemaining = [math]::Floor(($EndExclusiveUtc - $EvaluationUtc).TotalDays) + $ReleaseLine = '{0}.{1}' -f $TestProfile.powerShellMajor, $TestProfile.powerShellMinor + $Status = if ($EvaluationUtc -ge $EndExclusiveUtc) { + $ExpiredLines.Add($ReleaseLine) + 'Expired' + } elseif ($DaysRemaining -le [int]$TestMatrix.retirementWarningDays) { + 'RetiringSoon' + } else { + 'Supported' + } + + if ($Mode -eq 'Scheduled' -and $Status -eq 'RetiringSoon') { + Write-Warning "PowerShell $ReleaseLine retires on $($TestProfile.lifecycleEndDate) ($DaysRemaining day(s) remaining)." + } + + $Results.Add([PSCustomObject]@{ + PowerShellVersion = $TestProfile.powerShellVersion + ReleaseLine = $ReleaseLine + DotnetMajor = [int]$TestProfile.dotnetMajor + TargetFramework = $TestProfile.targetFramework + LifecycleEndDate = $TestProfile.lifecycleEndDate + DaysRemaining = $DaysRemaining + EvidenceAgeDays = [math]::Round($EvidenceAgeDays, 2) + Status = $Status + }) +} + +$Violations = [System.Collections.Generic.List[string]]::new() +if ($ExpiredLines.Count -gt 0) { + $Violations.Add("expired PowerShell lines: $($ExpiredLines -join ', ')") +} +if ($EvidenceIsStale) { + $EvidenceDescription = if ($LifecycleEvidence) { "live evidence generated $VerifiedTimestamp" } else { "last verified $VerifiedTimestamp" } + $Violations.Add("lifecycle evidence is stale; $EvidenceDescription, $([math]::Floor($EvidenceAgeDays)) day(s) ago") + if ($Mode -eq 'Scheduled') { + Write-Warning $Violations[$Violations.Count - 1] + } +} + +if ($Mode -eq 'Release' -and $Violations.Count -gt 0) { + throw "Runtime profile policy release check failed: $($Violations -join '; ')." +} +if ($Mode -eq 'Scheduled' -and $ExpiredLines.Count -gt 0) { + throw "Runtime profile policy contains expired PowerShell lines: $($ExpiredLines -join ', ')." +} + +if ($PassThru) { + $Results +} diff --git a/tools/Test-DLLPickleTfmAlignment.ps1 b/tools/Test-DLLPickleTfmAlignment.ps1 index 9638f5fd..6e422082 100644 --- a/tools/Test-DLLPickleTfmAlignment.ps1 +++ b/tools/Test-DLLPickleTfmAlignment.ps1 @@ -1,24 +1,18 @@ <# .SYNOPSIS - Asserts that bundled (preload) NuGet packages ship a net8.0-compatible assembly asset. + Assert that every preload package resolves an assembly asset for every supported TFM. .DESCRIPTION - Implements Step 0(b) of the tracked-dependency release lifecycle in docs/Architecture.md - section 8.2: the explicit "TFM-alignment" inspection. The Build gate (Step 0(a)) proves a - package restores and builds green under --locked-mode; this tool proves the complementary - half - that each preload package actually contains a target-framework asset net8.0 can - consume (net8.0 or a lower netX.0/netcoreapp asset, or a netstandard2.0/2.1/1.x asset), - rather than appearing to work only by luck of transitive resolution. + Implements Step 0(b) of the tracked-dependency release lifecycle. Policy mode reads NuGet's + restored project.assets.json and verifies the actual runtime asset selection for every preload + package under each target framework declared by the dependency policy. It does not approximate + NuGet compatibility with a handwritten TFM model. Two modes: - PackageDirectory: inspect a single extracted NuGet package directory (one with a lib/ - folder) and return its alignment result. - - Policy: resolve the preload set from build/dependency-policy.json, the restored versions - from packages.lock.json, locate each package under the NuGet global-packages folder, and - return an aggregate report (optionally failing in -Strict mode). - - This is a focused subset of NuGet's compatibility model sufficient for the in-scope MSAL + - IdentityModel families (all ship netstandard2.0 and/or net8.0); it is not a full resolver. + folder) against a selected portable target framework and return its alignment result. + - Policy: inspect NuGet's resolved target graph and selected assets for all supported TFMs, + then return an aggregate report (optionally failing in -Strict mode). .PARAMETER PackageDirectory Path to a single extracted NuGet package directory (containing a lib/ folder) to inspect. @@ -26,15 +20,17 @@ .PARAMETER PackageName Optional package name to report for the PackageDirectory mode. Defaults to the directory leaf. +.PARAMETER TargetFramework + Portable runtime target framework to evaluate in PackageDirectory mode. Defaults to net8.0. + .PARAMETER PolicyPath Path to the dependency policy JSON file (Policy mode). .PARAMETER LockFilePath Path to packages.lock.json, used to resolve each preload package's restored version (Policy mode). -.PARAMETER PackagesRoot - NuGet global-packages folder that holds the restored packages. Defaults to $env:NUGET_PACKAGES, - then to ~/.nuget/packages (Policy mode). +.PARAMETER ProjectAssetsPath + Restored NuGet project.assets.json used as the authority for TFM asset selection. .PARAMETER OutputPath Optional path where the JSON alignment report is written (Policy mode). @@ -45,6 +41,9 @@ .EXAMPLE ./tools/Test-DLLPickleTfmAlignment.ps1 -PackageDirectory ~/.nuget/packages/microsoft.identity.client/4.84.1 +.EXAMPLE + ./tools/Test-DLLPickleTfmAlignment.ps1 -PackageDirectory ~/.nuget/packages/microsoft.identity.client/4.84.1 -TargetFramework net10.0 + .EXAMPLE ./tools/Test-DLLPickleTfmAlignment.ps1 -OutputPath ./artifacts/upstreamCompatibility/tfm-alignment.json -Strict @@ -62,16 +61,21 @@ param( [Parameter(ParameterSetName = 'PackageDirectory')] [string]$PackageName, + [Parameter(ParameterSetName = 'PackageDirectory')] + [ValidatePattern('^net\d+\.\d+$')] + [string]$TargetFramework = 'net8.0', + [Parameter(ParameterSetName = 'Policy')] [ValidateNotNullOrEmpty()] - [string]$PolicyPath = (Join-Path -Path (Split-Path -Parent $PSScriptRoot) -ChildPath 'build\dependency-policy.json'), + [string]$PolicyPath = (Join-Path -Path (Split-Path -Parent $PSScriptRoot) -ChildPath 'build/dependency-policy.json'), [Parameter(ParameterSetName = 'Policy')] [ValidateNotNullOrEmpty()] - [string]$LockFilePath = (Join-Path -Path (Split-Path -Parent $PSScriptRoot) -ChildPath 'src\DLLPickle.Build\packages.lock.json'), + [string]$LockFilePath = (Join-Path -Path (Split-Path -Parent $PSScriptRoot) -ChildPath 'src/DLLPickle.Build/packages.lock.json'), [Parameter(ParameterSetName = 'Policy')] - [string]$PackagesRoot, + [ValidateNotNullOrEmpty()] + [string]$ProjectAssetsPath = (Join-Path -Path (Split-Path -Parent $PSScriptRoot) -ChildPath 'src/DLLPickle.Build/obj/project.assets.json'), [Parameter(ParameterSetName = 'Policy')] [string]$OutputPath, @@ -88,39 +92,49 @@ function Test-DLLPickleTargetFrameworkCompatible { param( [Parameter(Mandatory)] [AllowEmptyString()] - [string]$TargetFramework + [string]$TargetFramework, + + [Parameter(Mandatory)] + [string]$RuntimeTargetFramework ) $Moniker = ([string]$TargetFramework).Trim().ToLowerInvariant() + $RuntimeMoniker = ([string]$RuntimeTargetFramework).Trim().ToLowerInvariant() if ([string]::IsNullOrWhiteSpace($Moniker)) { return $false } + $RuntimeMatch = [regex]::Match($RuntimeMoniker, '^net(?\d+)\.\d+$') + if (-not $RuntimeMatch.Success) { + throw "PackageDirectory mode requires a portable .NET target framework such as net8.0: '$RuntimeTargetFramework'." + } + $RuntimeMajor = [int]$RuntimeMatch.Groups['major'].Value + # Reject OS-specific TFMs (e.g. net8.0-windows, net8.0-browser, net8.0-android). DLLPickle's - # bundle is validated as a PORTABLE net8.0 asset across Windows/Linux/macOS, so a package that - # ships only an OS-specific asset has no portable asset to preload and is not Step 0b-aligned. + # bundles are portable across Windows/Linux/macOS, so an OS-specific-only package has no + # portable asset to preload and is not Step 0b-aligned. if ($Moniker.Contains('-')) { return $false } - # .NET Standard (1.x-2.1): loadable on net8.0. + # .NET Standard (1.x-2.1): loadable on every supported modern .NET runtime. if ($Moniker -match '^netstandard\d+\.\d+$') { return $true } - # .NET Core 1.x-3.1 (netcoreapp): consumable by net8.0. + # .NET Core 1.x-3.1 (netcoreapp): consumable by every supported modern .NET runtime. if ($Moniker -match '^netcoreapp\d+\.\d+$') { return $true } - # .NET 5+ (netX.0, with a dot): consumable only up to the supported runtime major (8); - # a net9.0+ asset references a newer runtime contract and is not loadable on net8.0. + # .NET 5+ (netX.0, with a dot): consumable only when it does not exceed the selected + # portable runtime major. $NetCoreMatch = [regex]::Match($Moniker, '^net(\d+)\.\d+$') if ($NetCoreMatch.Success) { - return ([int]$NetCoreMatch.Groups[1].Value -le 8) + return ([int]$NetCoreMatch.Groups[1].Value -le $RuntimeMajor) } - # .NET Framework (net20-net48, no dot) is a different runtime, not loadable on net8.0. + # .NET Framework (net20-net48, no dot) is a different runtime, not loadable on modern .NET. # Anything else (unknown/garbage monikers) is fail-closed. return $false } @@ -170,7 +184,10 @@ function Test-DLLPickleSinglePackageAlignment { [string]$Name, [Parameter()] - [string]$ResolvedVersion + [string]$ResolvedVersion, + + [Parameter(Mandatory)] + [string]$TargetFramework ) $ResolvedName = if (-not [string]::IsNullOrWhiteSpace($Name)) { $Name } else { Split-Path -Path $PackagePath -Leaf } @@ -185,21 +202,23 @@ function Test-DLLPickleSinglePackageAlignment { $Lib = Get-DLLPickleLibTargetFramework -PackagePath $PackagePath if (-not $Lib.HasLib) { $IsAligned = $false - $Reason = 'No lib/ folder is present, so the package ships no net8.0/netstandard2.0 runtime asset.' + $Reason = "No lib/ folder is present, so the package ships no runtime asset compatible with $TargetFramework." } elseif ($Lib.IsFlatLib) { $IsAligned = $true $Available = @('lib') $Compatible = @('lib') - $Reason = 'Legacy flat lib/ layout: assemblies apply to any target framework, including net8.0.' + $Reason = "Legacy flat lib/ layout: assemblies apply to any target framework, including $TargetFramework." } else { $Available = @($Lib.TargetFrameworks) - $Compatible = @($Available | Where-Object { Test-DLLPickleTargetFrameworkCompatible -TargetFramework $_ }) + $Compatible = @($Available | Where-Object { + Test-DLLPickleTargetFrameworkCompatible -TargetFramework $_ -RuntimeTargetFramework $TargetFramework + }) if ($Compatible.Count -gt 0) { $IsAligned = $true - $Reason = "net8.0-compatible asset(s) present: $($Compatible -join ', ')." + $Reason = "$TargetFramework-compatible asset(s) present: $($Compatible -join ', ')." } else { $IsAligned = $false - $Reason = "No net8.0/netstandard2.0-compatible asset; lib/ ships only: $($Available -join ', ')." + $Reason = "No $TargetFramework-compatible runtime asset; lib/ ships only: $($Available -join ', ')." } } } @@ -207,6 +226,7 @@ function Test-DLLPickleSinglePackageAlignment { [PSCustomObject]@{ PackageName = $ResolvedName ResolvedVersion = $ResolvedVersion + TargetFramework = $TargetFramework PackageDirectory = $PackagePath IsAligned = $IsAligned CompatibleAssets = @($Compatible) @@ -223,22 +243,20 @@ function Get-DLLPickleResolvedPackageVersion { [object]$LockObject, [Parameter(Mandatory)] - [string]$Name + [string]$Name, + + [Parameter(Mandatory)] + [string]$TargetFramework ) if (-not $LockObject.dependencies) { return $null } - # Prefer the net8.0 dependency group, then any other, when reading the resolved version. - $Groups = @($LockObject.dependencies.PSObject.Properties | - Sort-Object -Property { if ($_.Name -eq 'net8.0') { 0 } else { 1 } }) - - foreach ($Group in $Groups) { + $Group = $LockObject.dependencies.PSObject.Properties | Where-Object Name -eq $TargetFramework | Select-Object -First 1 + if ($Group) { $Entry = $Group.Value.PSObject.Properties | Where-Object { $_.Name -eq $Name } | Select-Object -First 1 - if ($Entry) { - return [string]$Entry.Value.resolved - } + if ($Entry) { return [string]$Entry.Value.resolved } } return $null @@ -246,53 +264,102 @@ function Get-DLLPickleResolvedPackageVersion { if ($PSCmdlet.ParameterSetName -eq 'PackageDirectory') { $ResolvedDirectory = (Resolve-Path -LiteralPath $PackageDirectory).Path - Test-DLLPickleSinglePackageAlignment -PackagePath $ResolvedDirectory -Name $PackageName + Test-DLLPickleSinglePackageAlignment -PackagePath $ResolvedDirectory -Name $PackageName -TargetFramework $TargetFramework return } -if ([string]::IsNullOrWhiteSpace($PackagesRoot)) { - $PackagesRoot = if (-not [string]::IsNullOrWhiteSpace($env:NUGET_PACKAGES)) { - $env:NUGET_PACKAGES - } else { - Join-Path -Path $HOME -ChildPath '.nuget' -AdditionalChildPath 'packages' - } -} - $ResolvedPolicyPath = (Resolve-Path -LiteralPath $PolicyPath).Path $ResolvedLockPath = (Resolve-Path -LiteralPath $LockFilePath).Path +$ResolvedProjectAssetsPath = (Resolve-Path -LiteralPath $ProjectAssetsPath).Path $Policy = Get-Content -LiteralPath $ResolvedPolicyPath -Raw | ConvertFrom-Json $Lock = Get-Content -LiteralPath $ResolvedLockPath -Raw | ConvertFrom-Json +$ProjectAssets = Get-Content -LiteralPath $ResolvedProjectAssetsPath -Raw | ConvertFrom-Json +$RuntimeProfiles = if ($Policy.PSObject.Properties.Name -contains 'runtimeProfiles') { + @($Policy.runtimeProfiles) +} else { + @() +} +if ($RuntimeProfiles.Count -eq 0) { + throw "Dependency policy '$PolicyPath' must declare at least one runtimeProfiles entry." +} +$DeclaredTargetFrameworks = @( + foreach ($RuntimeProfile in $RuntimeProfiles) { + $DeclaredTargetFramework = [string]$RuntimeProfile.targetFramework + if ([string]::IsNullOrWhiteSpace($DeclaredTargetFramework)) { + throw "Dependency policy '$PolicyPath' declares a runtimeProfiles entry without a targetFramework." + } + $DeclaredTargetFramework + } +) +$TargetFrameworks = @($DeclaredTargetFrameworks | Sort-Object -Unique) + +$PackageResults = foreach ($TargetFramework in $TargetFrameworks) { + $TargetGraphProperty = $ProjectAssets.targets.PSObject.Properties | + Where-Object Name -eq $TargetFramework | + Select-Object -First 1 + + foreach ($Pin in @($Policy.preload)) { + $Name = [string]$Pin.packageName + $Version = Get-DLLPickleResolvedPackageVersion -LockObject $Lock -Name $Name -TargetFramework $TargetFramework + $SelectedAssets = @() + $Reason = $null + + if ([string]::IsNullOrWhiteSpace($Version)) { + $Reason = "No resolved version for '$Name' was found in '$ResolvedLockPath' under '$TargetFramework'." + } elseif (-not $TargetGraphProperty) { + $Reason = "NuGet project.assets.json contains no restored target graph for '$TargetFramework'." + } else { + $PackageKey = '{0}/{1}' -f $Name, $Version + $PackageProperty = $TargetGraphProperty.Value.PSObject.Properties | + Where-Object Name -ieq $PackageKey | + Select-Object -First 1 + if (-not $PackageProperty) { + $Reason = "NuGet selected no '$PackageKey' entry for '$TargetFramework'." + } else { + $RuntimeAssets = if ($PackageProperty.Value.runtime) { + @($PackageProperty.Value.runtime.PSObject.Properties.Name | Where-Object { $_ -match '\.dll$' }) + } else { + @() + } + $CompileAssets = if ($PackageProperty.Value.compile) { + @($PackageProperty.Value.compile.PSObject.Properties.Name | Where-Object { $_ -match '\.dll$' }) + } else { + @() + } + $SelectedAssets = @($RuntimeAssets) + if ($RuntimeAssets.Count -gt 0) { + $Reason = "NuGet selected asset(s) for ${TargetFramework}: $($SelectedAssets -join ', ')." + } elseif ($CompileAssets.Count -gt 0) { + $Reason = "NuGet selected compile-only asset(s) for ${TargetFramework}, but no runtime assembly can be copied or loaded: $($CompileAssets -join ', ')." + } else { + $Reason = "NuGet selected '$PackageKey' for '$TargetFramework' but no assembly compile/runtime asset." + } + } + } -$PackageResults = foreach ($Pin in @($Policy.preload)) { - $Name = [string]$Pin.packageName - $Version = Get-DLLPickleResolvedPackageVersion -LockObject $Lock -Name $Name - - if ([string]::IsNullOrWhiteSpace($Version)) { [PSCustomObject]@{ PackageName = $Name - ResolvedVersion = $null - PackageDirectory = $null - IsAligned = $false - CompatibleAssets = @() - AvailableAssets = @() - Reason = "No resolved version for '$Name' was found in '$ResolvedLockPath'." + ResolvedVersion = $Version + TargetFramework = $TargetFramework + IsAligned = $SelectedAssets.Count -gt 0 + SelectedAssets = @($SelectedAssets) + CompatibleAssets = @($SelectedAssets) + AvailableAssets = @($SelectedAssets) + Reason = $Reason } - continue } - - $PackagePath = Join-Path -Path $PackagesRoot -ChildPath $Name.ToLowerInvariant() -AdditionalChildPath $Version.ToLowerInvariant() - Test-DLLPickleSinglePackageAlignment -PackagePath $PackagePath -Name $Name -ResolvedVersion $Version } $PackageResultArray = @($PackageResults) -$Misaligned = @($PackageResultArray | Where-Object { -not $_.IsAligned } | ForEach-Object { $_.PackageName }) +$Misaligned = @($PackageResultArray | Where-Object { -not $_.IsAligned } | ForEach-Object { '{0}/{1}' -f $_.TargetFramework, $_.PackageName }) $IsAligned = ($PackageResultArray.Count -gt 0) -and ($Misaligned.Count -eq 0) $Report = [PSCustomObject]@{ GeneratedAtUtc = [System.DateTimeOffset]::UtcNow.ToString('o') PolicyPath = $ResolvedPolicyPath LockFilePath = $ResolvedLockPath - PackagesRoot = $PackagesRoot + ProjectAssetsPath = $ResolvedProjectAssetsPath + TargetFrameworks = $TargetFrameworks IsAligned = $IsAligned Packages = $PackageResultArray Misaligned = $Misaligned @@ -307,7 +374,7 @@ if (-not [string]::IsNullOrWhiteSpace($OutputPath)) { } if ($Strict.IsPresent -and -not $IsAligned) { - throw ("TFM alignment check failed: the following preload package(s) ship no net8.0/netstandard2.0-compatible asset: {0}." -f ($Misaligned -join ', ')) + throw ("TFM alignment check failed: NuGet selected no assembly asset for: {0}." -f ($Misaligned -join ', ')) } $Report diff --git a/tools/Update-DLLPickleDependencyPins.ps1 b/tools/Update-DLLPickleDependencyPins.ps1 index 64bd7856..766b1d85 100644 --- a/tools/Update-DLLPickleDependencyPins.ps1 +++ b/tools/Update-DLLPickleDependencyPins.ps1 @@ -12,7 +12,9 @@ applied. .PARAMETER InventoryPath - Path to a JSON report produced by Get-DLLPickleUpstreamInventory.ps1. + One or more JSON reports produced by Get-DLLPickleUpstreamInventory.ps1. When + profile-aware reports are supplied, every target framework is reconciled across + its operating-system inventories before a common pin is changed. .PARAMETER PolicyPath Path to the dependency policy JSON file. @@ -24,7 +26,8 @@ Path where the JSON candidate report is written. .PARAMETER Restore - Runs dotnet restore --force-evaluate when a project file change is applied. + Runs dotnet restore --force-evaluate when a project file change is applied or + when an unchanged floating reference must be reevaluated against upstream state. .EXAMPLE ./tools/Update-DLLPickleDependencyPins.ps1 -InventoryPath ./artifacts/upstream/inventory.json -Restore @@ -37,19 +40,19 @@ param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] - [string]$InventoryPath, + [string[]]$InventoryPath, [Parameter()] [ValidateNotNullOrEmpty()] - [string]$PolicyPath = (Join-Path -Path (Split-Path -Parent $PSScriptRoot) -ChildPath 'build\dependency-policy.json'), + [string]$PolicyPath = (Join-Path -Path (Split-Path -Parent $PSScriptRoot) -ChildPath 'build/dependency-policy.json'), [Parameter()] [ValidateNotNullOrEmpty()] - [string]$ProjectPath = (Join-Path -Path (Split-Path -Parent $PSScriptRoot) -ChildPath 'src\DLLPickle.Build\DLLPickle.csproj'), + [string]$ProjectPath = (Join-Path -Path (Split-Path -Parent $PSScriptRoot) -ChildPath 'src/DLLPickle.Build/DLLPickle.csproj'), [Parameter()] [ValidateNotNullOrEmpty()] - [string]$OutputPath = (Join-Path -Path (Split-Path -Parent $PSScriptRoot) -ChildPath 'artifacts\upstreamCompatibility\candidate-report.json'), + [string]$OutputPath = (Join-Path -Path (Split-Path -Parent $PSScriptRoot) -ChildPath 'artifacts/upstreamCompatibility/candidate-report.json'), [Parameter()] [switch]$Restore @@ -86,31 +89,80 @@ function Get-DLLPickleCurrentPackageReference { [string]$TargetFramework ) + function Test-DLLPickleTargetFrameworkCondition { + param( + [Parameter()] + [AllowEmptyString()] + [string]$Condition, + + [Parameter(Mandatory)] + [string]$Framework + ) + + if ([string]::IsNullOrWhiteSpace($Condition) -or $Condition -notmatch '\$\(TargetFramework\)') { + return $true + } + + $ConditionMatch = [regex]::Match( + $Condition, + "^\s*'?\$\(TargetFramework\)'?\s*(?==|!=)\s*'(?[^']+)'\s*$" + ) + if (-not $ConditionMatch.Success) { + throw "Unsupported TargetFramework condition in PackageReference automation: $Condition" + } + + $ConditionFramework = $ConditionMatch.Groups['framework'].Value + if ($ConditionMatch.Groups['operator'].Value -eq '==') { + return $Framework -eq $ConditionFramework + } + return $Framework -ne $ConditionFramework + } + + $ResolvedReferences = [System.Collections.Generic.List[object]]::new() + $ItemGroupCondition = $null for ($Index = 0; $Index -lt $ProjectContent.Count; $Index++) { $Line = $ProjectContent[$Index] + if ($Line -match '') { + $ItemGroupCondition = $null + } } - return $null + if ($ResolvedReferences.Count -gt 1) { + throw "PackageReference '$PackageName' resolves ambiguously for target framework '$TargetFramework' at project lines $(@($ResolvedReferences.Index | ForEach-Object { $_ + 1 }) -join ', ')." + } + + return @($ResolvedReferences)[0] } function ConvertTo-DLLPickleUpdatedPackageReferenceContent { @@ -132,102 +184,293 @@ function ConvertTo-DLLPickleUpdatedPackageReferenceContent { $UpdatedContent } -$ResolvedInventoryPath = (Resolve-Path -LiteralPath $InventoryPath).Path +function Get-DLLPicklePackageReferenceMajor { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string]$Version + ) + + $Match = [regex]::Match($Version.Trim(), '^\[?(?\d+)(?:\.(?:\d+|\*)){1,3}\]?$') + if (-not $Match.Success) { + throw "PackageReference version '$Version' is not a supported exact or major-floating version." + } + + return [int]$Match.Groups['major'].Value +} + +function Get-DLLPicklePinTargetFramework { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [object]$Pin + ) + + $TargetFrameworks = @( + if ($Pin.PSObject.Properties.Name -contains 'targetFrameworks') { + @($Pin.targetFrameworks) + } elseif ($Pin.PSObject.Properties.Name -contains 'targetFramework') { + @($Pin.targetFramework) + } + ) | + ForEach-Object { [string]$_ } | + Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | + Select-Object -Unique + + if ($TargetFrameworks.Count -eq 0) { + throw "Dependency pin '$($Pin.packageName)' does not declare targetFrameworks." + } + + return @($TargetFrameworks) +} + +$ResolvedInventoryPaths = @($InventoryPath | ForEach-Object { (Resolve-Path -LiteralPath $_).Path } | Sort-Object -Unique) $ResolvedPolicyPath = (Resolve-Path -LiteralPath $PolicyPath).Path $ResolvedProjectPath = (Resolve-Path -LiteralPath $ProjectPath).Path -$Inventory = Get-Content -LiteralPath $ResolvedInventoryPath -Raw | ConvertFrom-Json +$Inventories = @( + foreach ($ResolvedInventoryPath in $ResolvedInventoryPaths) { + $Inventory = Get-Content -LiteralPath $ResolvedInventoryPath -Raw | ConvertFrom-Json + [PSCustomObject]@{ + Path = $ResolvedInventoryPath + Data = $Inventory + ProfileKey = if ($Inventory.PSObject.Properties.Name -contains 'ProfileKey') { [string]$Inventory.ProfileKey } else { $null } + TargetFramework = if ($Inventory.Profile -and $Inventory.Profile.PSObject.Properties.Name -contains 'TargetFramework') { [string]$Inventory.Profile.TargetFramework } else { $null } + } + } +) $Policy = Get-Content -LiteralPath $ResolvedPolicyPath -Raw | ConvertFrom-Json $ProjectContent = @(Get-Content -LiteralPath $ResolvedProjectPath) $ProjectChanged = $false +$RestoreRequired = $false +$RestoreExecuted = $false $Changes = New-Object System.Collections.Generic.List[object] $Warnings = New-Object System.Collections.Generic.List[string] foreach ($Pin in @($Policy.preload)) { + $TargetFrameworks = @(Get-DLLPicklePinTargetFramework -Pin $Pin) $SourceModuleLookup = @{} foreach ($SourceModule in @($Pin.sourceModules)) { $SourceModuleLookup[[string]$SourceModule] = $true } - $CandidateAssemblies = @( - foreach ($Module in @($Inventory.Modules)) { - if (-not $SourceModuleLookup[[string]$Module.Name]) { - continue - } + $TfmCandidates = [System.Collections.Generic.List[object]]::new() + $CandidateResolutionFailed = $false + foreach ($TargetFramework in $TargetFrameworks) { + $RelevantInventories = @($Inventories | Where-Object { + [string]::IsNullOrWhiteSpace($_.TargetFramework) -or $_.TargetFramework -eq $TargetFramework + }) + if ($RelevantInventories.Count -eq 0) { + $Warnings.Add("No upstream inventory was supplied for target framework '$TargetFramework'.") + $CandidateResolutionFailed = $true + continue + } - foreach ($Assembly in @($Module.TrackedAssemblies)) { - if ([string]$Assembly.Name -eq [string]$Pin.assemblyName) { - [PSCustomObject]@{ - ModuleName = [string]$Module.Name - ModuleVersion = [string]$Module.Version - AssemblyName = [string]$Assembly.Name - AssemblyVersion = [string]$Assembly.Version - PackageVersion = ConvertTo-DLLPickleNuGetVersion -AssemblyVersion ([string]$Assembly.Version) - RelativePath = [string]$Assembly.RelativePath + $PerInventoryCandidates = @( + foreach ($InventoryRecord in $RelevantInventories) { + $Candidates = @( + foreach ($Module in @($InventoryRecord.Data.Modules)) { + if (-not $SourceModuleLookup[[string]$Module.Name]) { + continue + } + + foreach ($Assembly in @($Module.TrackedAssemblies)) { + if ([string]$Assembly.Name -eq [string]$Pin.assemblyName) { + [PSCustomObject]@{ + InventoryPath = [string]$InventoryRecord.Path + ProfileKey = [string]$InventoryRecord.ProfileKey + TargetFramework = $TargetFramework + ModuleName = [string]$Module.Name + ModuleVersion = [string]$Module.Version + AssemblyName = [string]$Assembly.Name + AssemblyVersion = [string]$Assembly.Version + PackageVersion = ConvertTo-DLLPickleNuGetVersion -AssemblyVersion ([string]$Assembly.Version) + RelativePath = [string]$Assembly.RelativePath + } + } + } } + ) + if ($Candidates.Count -eq 0) { + $Warnings.Add(("No upstream assembly '{0}' was found for target framework '{1}' in source modules: {2}" -f $Pin.assemblyName, $TargetFramework, (@($Pin.sourceModules) -join ', '))) + $CandidateResolutionFailed = $true + continue } + $Candidates | Sort-Object -Property { [version]$_.AssemblyVersion } -Descending | Select-Object -First 1 } + ) + if ($PerInventoryCandidates.Count -ne $RelevantInventories.Count) { + $CandidateResolutionFailed = $true + continue } - ) - if ($CandidateAssemblies.Count -eq 0) { - $Warnings.Add(("No upstream assembly '{0}' was found in source modules: {1}" -f $Pin.assemblyName, (@($Pin.sourceModules) -join ', '))) + $DistinctVersions = @($PerInventoryCandidates.PackageVersion | Sort-Object -Unique) + $TfmCandidates.Add([PSCustomObject]@{ + TargetFramework = $TargetFramework + PackageVersion = [string]($PerInventoryCandidates | Sort-Object -Property { [version]$_.PackageVersion } -Descending | Select-Object -First 1).PackageVersion + CrossPlatformConsistent = $DistinctVersions.Count -eq 1 + ProfileCandidates = @($PerInventoryCandidates) + }) + if ($DistinctVersions.Count -ne 1) { + $Warnings.Add(("Upstream assembly '{0}' resolves to inconsistent package versions for target framework '{1}': {2}. No automatic update was applied." -f $Pin.assemblyName, $TargetFramework, ($DistinctVersions -join ', '))) + } + } + + if ($CandidateResolutionFailed -or $TfmCandidates.Count -ne $TargetFrameworks.Count) { continue } - $TargetAssembly = $CandidateAssemblies | - Sort-Object -Property { [version]$_.AssemblyVersion } -Descending | - Select-Object -First 1 - $TargetVersion = [string]$TargetAssembly.PackageVersion $IsCapped = -not [string]::IsNullOrWhiteSpace([string]$Pin.maximumPackageVersion) if ($IsCapped) { $MaximumPackageVersion = [string]$Pin.maximumPackageVersion - if ([version]$TargetVersion -gt [version]$MaximumPackageVersion) { + $HighestCandidateVersion = @($TfmCandidates.PackageVersion | Sort-Object { [version]$_ } -Descending)[0] + $CappedTargetFrameworks = @($TfmCandidates | Where-Object { [version]$_.PackageVersion -gt [version]$MaximumPackageVersion } | ForEach-Object TargetFramework) + if ($CappedTargetFrameworks.Count -gt 0) { $Warnings.Add(( - "PackageReference '{0}' candidate '{1}' exceeds maximum '{2}' for target framework '{3}'; using maximum version." -f + "PackageReference '{0}' candidate '{1}' exceeds maximum '{2}' for target frameworks '{3}'; using maximum version." -f $Pin.packageName, - $TargetVersion, + $HighestCandidateVersion, $MaximumPackageVersion, - $Pin.targetFramework + ($CappedTargetFrameworks -join ', ') )) - $TargetVersion = $MaximumPackageVersion } } $VersionPolicy = if ($Pin.versionPolicy) { [string]$Pin.versionPolicy } else { 'exact' } - # A minorPatchFloat rule normally writes a floating 'N.*' reference. But when the entry also - # declares maximumPackageVersion, a floating reference would let restore resolve ABOVE the cap, - # so a capped entry is written as an exact '[x.y.z]' pinned at the capped target to enforce it. - $FormattedVersion = if ($VersionPolicy -eq 'minorPatchFloat' -and -not $IsCapped) { - '{0}.*' -f ([version]$TargetVersion).Major - } else { - '[{0}]' -f $TargetVersion + foreach ($TfmCandidate in $TfmCandidates) { + $TargetVersion = [string]$TfmCandidate.PackageVersion + if ($IsCapped -and [version]$TargetVersion -gt [version]$MaximumPackageVersion) { + $TargetVersion = $MaximumPackageVersion + } + # A capped float must be exact; otherwise restore could resolve above the cap. + $TfmCandidate | Add-Member -NotePropertyName FormattedVersion -NotePropertyValue $(if ($VersionPolicy -eq 'minorPatchFloat' -and -not $IsCapped) { + '{0}.*' -f ([version]$TargetVersion).Major + } else { + '[{0}]' -f $TargetVersion + }) } - $CurrentReference = Get-DLLPickleCurrentPackageReference -ProjectContent $ProjectContent -PackageName ([string]$Pin.packageName) -TargetFramework ([string]$Pin.targetFramework) - if (-not $CurrentReference) { - $Warnings.Add(("PackageReference '{0}' for target framework '{1}' was not found; no automatic insert was attempted." -f $Pin.packageName, $Pin.targetFramework)) + $CurrentReferences = @( + foreach ($TargetFramework in $TargetFrameworks) { + $Reference = Get-DLLPickleCurrentPackageReference -ProjectContent $ProjectContent -PackageName ([string]$Pin.packageName) -TargetFramework $TargetFramework + [PSCustomObject]@{ + TargetFramework = $TargetFramework + Reference = $Reference + } + } + ) + $MissingTargetFrameworks = @($CurrentReferences | Where-Object { -not $_.Reference } | Select-Object -ExpandProperty TargetFramework) + + if ($MissingTargetFrameworks.Count -gt 0) { + $Warnings.Add(("PackageReference '{0}' was not found for target frameworks '{1}'; no automatic insert or partial update was attempted." -f $Pin.packageName, ($MissingTargetFrameworks -join ', '))) continue } + $MajorTransitions = @( + if ($VersionPolicy -eq 'minorPatchFloat' -and -not $IsCapped) { + foreach ($CurrentReference in $CurrentReferences) { + $TfmCandidate = @($TfmCandidates | Where-Object TargetFramework -EQ $CurrentReference.TargetFramework)[0] + $CurrentMajor = Get-DLLPicklePackageReferenceMajor -Version ([string]$CurrentReference.Reference.Version) + $CandidateMajor = ([version][string]$TfmCandidate.PackageVersion).Major + if ($CurrentMajor -ne $CandidateMajor) { + [PSCustomObject]@{ + TargetFramework = [string]$CurrentReference.TargetFramework + CurrentVersion = [string]$CurrentReference.Reference.Version + CandidateVersion = '{0}.*' -f $CandidateMajor + } + } + } + } + ) + $MajorTransitionRequired = $MajorTransitions.Count -gt 0 + + $UniqueReferenceIndices = @($CurrentReferences.Reference.Index | Select-Object -Unique) + $UsesConditionalReferences = $UniqueReferenceIndices.Count -gt 1 + $DistinctCandidateVersions = @($TfmCandidates.FormattedVersion | Sort-Object -Unique) + $ConditionalPinRequired = $DistinctCandidateVersions.Count -gt 1 -and -not $UsesConditionalReferences + $CrossPlatformConsistent = @($TfmCandidates | Where-Object { -not $_.CrossPlatformConsistent }).Count -eq 0 + $TfmResults = @( + foreach ($CurrentReference in $CurrentReferences) { + $TfmCandidate = @($TfmCandidates | Where-Object TargetFramework -EQ $CurrentReference.TargetFramework)[0] + [PSCustomObject]@{ + TargetFramework = [string]$CurrentReference.TargetFramework + CurrentVersion = [string]$CurrentReference.Reference.Version + CandidateVersion = [string]$TfmCandidate.FormattedVersion + ReferenceIndex = [int]$CurrentReference.Reference.Index + CrossPlatformConsistent = [bool]$TfmCandidate.CrossPlatformConsistent + Applied = $false + } + } + ) + + $AllProfileCandidates = @($TfmCandidates.ProfileCandidates) + $TargetAssembly = $AllProfileCandidates | Sort-Object -Property { [version]$_.AssemblyVersion } -Descending | Select-Object -First 1 + $Change = [PSCustomObject]@{ PackageName = [string]$Pin.packageName - TargetFramework = [string]$Pin.targetFramework - CurrentVersion = [string]$CurrentReference.Version - CandidateVersion = $FormattedVersion + TargetFrameworks = @($TargetFrameworks) + CurrentVersions = @($CurrentReferences.Reference.Version | Select-Object -Unique) + CandidateVersion = if ($DistinctCandidateVersions.Count -eq 1) { [string]$DistinctCandidateVersions[0] } else { $null } + CandidateVersions = @($DistinctCandidateVersions) SourceModule = [string]$TargetAssembly.ModuleName SourceModuleVersion = [string]$TargetAssembly.ModuleVersion SourceAssemblyVersion = [string]$TargetAssembly.AssemblyVersion + UsesConditionalReferences = $UsesConditionalReferences + ConditionalPinRequired = $ConditionalPinRequired + MajorTransitionRequired = $MajorTransitionRequired + CrossPlatformConsistent = $CrossPlatformConsistent + ReviewRequired = $UsesConditionalReferences -or $ConditionalPinRequired -or $MajorTransitionRequired -or -not $CrossPlatformConsistent + TfmResults = @($TfmResults) + RestoreRequired = $false Applied = $false Reason = [string]$Pin.reason } - if ([string]$CurrentReference.Version -ne $FormattedVersion) { - if ($PSCmdlet.ShouldProcess($ResolvedProjectPath, ("Update {0} {1} from {2} to {3}" -f $Pin.packageName, $Pin.targetFramework, $CurrentReference.Version, $FormattedVersion))) { - $ProjectContent = @(ConvertTo-DLLPickleUpdatedPackageReferenceContent -ProjectContent $ProjectContent -Index $CurrentReference.Index -NewVersion $FormattedVersion) + if ($ConditionalPinRequired) { + $Warnings.Add(("PackageReference '{0}' requires different versions by target framework ({1}); introducing conditional references requires maintainer review and was not automated." -f $Pin.packageName, ($DistinctCandidateVersions -join ', '))) + $Changes.Add($Change) + continue + } + if (-not $CrossPlatformConsistent) { + $Changes.Add($Change) + continue + } + if ($MajorTransitionRequired) { + $TransitionSummary = @( + $MajorTransitions | ForEach-Object { + '{0}: {1} -> {2}' -f $_.TargetFramework, $_.CurrentVersion, $_.CandidateVersion + } + ) -join '; ' + $Warnings.Add(("PackageReference '{0}' floating candidate crosses a package major ({1}); maintainer review is required and no automatic update was applied." -f $Pin.packageName, $TransitionSummary)) + $Changes.Add($Change) + continue + } + if ($VersionPolicy -eq 'minorPatchFloat' -and -not $IsCapped) { + $RestoreRequired = $true + $Change.RestoreRequired = $true + } + + foreach ($ReferenceIndex in $UniqueReferenceIndices) { + $ReferencesAtIndex = @($CurrentReferences | Where-Object { $_.Reference.Index -eq $ReferenceIndex }) + $CurrentVersion = [string]$ReferencesAtIndex[0].Reference.Version + $ReferenceCandidateVersions = @($TfmResults | Where-Object ReferenceIndex -EQ $ReferenceIndex | ForEach-Object CandidateVersion | Sort-Object -Unique) + if ($ReferenceCandidateVersions.Count -ne 1) { + throw "PackageReference '$($Pin.packageName)' maps one project entry to incompatible target-framework candidates." + } + $FormattedVersion = [string]$ReferenceCandidateVersions[0] + if ($CurrentVersion -eq $FormattedVersion) { + continue + } + + $ReferenceTargetFrameworks = @($ReferencesAtIndex.TargetFramework) + if ($PSCmdlet.ShouldProcess($ResolvedProjectPath, ("Update {0} for {1} from {2} to {3}" -f $Pin.packageName, ($ReferenceTargetFrameworks -join ', '), $CurrentVersion, $FormattedVersion))) { + $ProjectContent = @(ConvertTo-DLLPickleUpdatedPackageReferenceContent -ProjectContent $ProjectContent -Index $ReferenceIndex -NewVersion $FormattedVersion) $ProjectChanged = $true $Change.Applied = $true + foreach ($TfmResult in @($Change.TfmResults | Where-Object { $_.ReferenceIndex -eq $ReferenceIndex })) { + $TfmResult.Applied = $true + } } } @@ -236,17 +479,21 @@ foreach ($Pin in @($Policy.preload)) { $BlockedFindings = @( foreach ($BlockedAssembly in @($Policy.blockedPreloadAssemblies)) { - foreach ($Module in @($Inventory.Modules)) { - foreach ($Assembly in @($Module.TrackedAssemblies)) { - if ([string]$Assembly.Name -eq [string]$BlockedAssembly.assemblyName) { - [PSCustomObject]@{ - AssemblyName = [string]$Assembly.Name - Version = [string]$Assembly.Version - ModuleName = [string]$Module.Name - ModuleVersion = [string]$Module.Version - RelativePath = [string]$Assembly.RelativePath - Action = [string]$BlockedAssembly.updateMode - Reason = [string]$BlockedAssembly.reason + foreach ($InventoryRecord in $Inventories) { + foreach ($Module in @($InventoryRecord.Data.Modules)) { + foreach ($Assembly in @($Module.TrackedAssemblies)) { + if ([string]$Assembly.Name -eq [string]$BlockedAssembly.assemblyName) { + [PSCustomObject]@{ + AssemblyName = [string]$Assembly.Name + Version = [string]$Assembly.Version + ModuleName = [string]$Module.Name + ModuleVersion = [string]$Module.Version + RelativePath = [string]$Assembly.RelativePath + ProfileKey = [string]$InventoryRecord.ProfileKey + TargetFrameworks = @($BlockedAssembly.targetFrameworks) + Action = [string]$BlockedAssembly.updateMode + Reason = [string]$BlockedAssembly.reason + } } } } @@ -256,8 +503,11 @@ $BlockedFindings = @( if ($ProjectChanged) { Set-Content -LiteralPath $ResolvedProjectPath -Value $ProjectContent -Encoding UTF8 +} - if ($Restore.IsPresent) { +if ($Restore.IsPresent -and ($ProjectChanged -or $RestoreRequired)) { + $RestoreApproved = $ProjectChanged -or $PSCmdlet.ShouldProcess($ResolvedProjectPath, 'Reevaluate unchanged floating PackageReference entries and refresh the lock file') + if ($RestoreApproved) { $ProjectDirectory = Split-Path -Path $ResolvedProjectPath -Parent Push-Location -LiteralPath $ProjectDirectory try { @@ -265,6 +515,7 @@ if ($ProjectChanged) { if ($LASTEXITCODE -ne 0) { throw "dotnet restore failed with exit code $LASTEXITCODE." } + $RestoreExecuted = $true } finally { Pop-Location } @@ -273,10 +524,13 @@ if ($ProjectChanged) { $Report = [PSCustomObject]@{ GeneratedAtUtc = [System.DateTimeOffset]::UtcNow.ToString('o') - InventoryPath = $ResolvedInventoryPath + InventoryPaths = @($ResolvedInventoryPaths) PolicyPath = $ResolvedPolicyPath ProjectPath = $ResolvedProjectPath ProjectChanged = $ProjectChanged + RestoreRequired = $RestoreRequired + RestoreExecuted = $RestoreExecuted + ReviewRequired = @($Changes | Where-Object ReviewRequired).Count -gt 0 Changes = @($Changes.ToArray()) BlockedFindings = @($BlockedFindings) Warnings = @($Warnings.ToArray()) diff --git a/tools/Update-DLLPicklePowerShellTestMatrix.ps1 b/tools/Update-DLLPicklePowerShellTestMatrix.ps1 new file mode 100644 index 00000000..9b6e65cb --- /dev/null +++ b/tools/Update-DLLPicklePowerShellTestMatrix.ps1 @@ -0,0 +1,188 @@ +<# +.SYNOPSIS +Builds a validated exact-patch matrix proposal from support-update discovery evidence. + +.DESCRIPTION +Preparation mode writes a non-authoritative candidate matrix used only to provision +checksum-verified official archives. The exact bundled .NET patch is marked pending +until those archives report their runtime identities. Finalization requires one +runtime identity for every declared operating-system lane of every patch update, +proves the identities agree on the bundled .NET runtime, and writes the reviewable +matrix proposal. + +This command only writes local files. It never commits, pushes, opens a pull request, +or modifies an issue. +#> + +[CmdletBinding(DefaultParameterSetName = 'Finalize')] +[OutputType([pscustomobject])] +param ( + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$TestMatrixPath = (Join-Path (Split-Path -Parent $PSScriptRoot) 'build/powershell-test-matrix.json'), + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$UpdateReportPath, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$OutputPath, + + [Parameter(Mandatory, ParameterSetName = 'Prepare')] + [switch]$PrepareCandidate, + + [Parameter(Mandatory, ParameterSetName = 'Finalize')] + [ValidateNotNullOrEmpty()] + [string[]]$RuntimeIdentityPath, + + [Parameter(ParameterSetName = 'Finalize')] + [datetime]$VerifiedAtUtc = [datetime]::UtcNow +) + +$ErrorActionPreference = 'Stop' +$IsPreparation = $PrepareCandidate.IsPresent +foreach ($Path in @($TestMatrixPath, $UpdateReportPath)) { + if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { + throw "Required PowerShell matrix update input was not found: $Path" + } +} + +$Matrix = Get-Content -LiteralPath $TestMatrixPath -Raw | ConvertFrom-Json -ErrorAction Stop +$Report = Get-Content -LiteralPath $UpdateReportPath -Raw | ConvertFrom-Json -ErrorAction Stop +$PatchUpdates = @($Report.PatchUpdates) +if ($PatchUpdates.Count -eq 0) { + throw 'The support-update report contains no servicing patch proposal.' +} +if (-not $Report.MatrixOnlyUpdateAvailable -or $Report.SupportContractReviewRequired) { + throw 'A patch-only matrix proposal cannot include a new or retiring support line.' +} +if (@($PatchUpdates | Where-Object { -not $_.ChecksumsComplete }).Count -gt 0) { + throw 'Every proposed official archive must have a complete SHA-256 digest.' +} + +$IdentityRecords = @() +if (-not $IsPreparation) { + $IdentityFiles = @( + foreach ($IdentityPath in $RuntimeIdentityPath) { + if (Test-Path -LiteralPath $IdentityPath -PathType Container) { + Get-ChildItem -LiteralPath $IdentityPath -File -Filter '*.json' -Recurse + } elseif (Test-Path -LiteralPath $IdentityPath -PathType Leaf) { + Get-Item -LiteralPath $IdentityPath + } else { + throw "Runtime identity input was not found: $IdentityPath" + } + } + ) + foreach ($IdentityFile in $IdentityFiles) { + $Identity = Get-Content -LiteralPath $IdentityFile.FullName -Raw | ConvertFrom-Json -ErrorAction Stop + $RequiredIdentityProperties = @('PowerShellVersion', 'DotNetVersion', 'Platform', 'Architecture') + $MissingIdentityProperties = @($RequiredIdentityProperties | Where-Object { + $Identity.PSObject.Properties.Name -notcontains $_ -or + [string]::IsNullOrWhiteSpace([string]$Identity.$_) + }) + if ($MissingIdentityProperties.Count -gt 0) { + Write-Warning "Runtime identity file '$($IdentityFile.FullName)' is missing required value(s): $($MissingIdentityProperties -join ', '); the file will not be used." + continue + } + $IdentityRecords += $Identity + } +} + +$UpdatedLines = [System.Collections.Generic.List[object]]::new() +foreach ($PatchUpdate in $PatchUpdates) { + $CurrentVersion = [string]$PatchUpdate.CurrentVersion + $CandidateVersion = [string]$PatchUpdate.CandidateVersion + $MatrixProfile = @($Matrix.profiles | Where-Object powerShellVersion -EQ $CurrentVersion) + if ($MatrixProfile.Count -ne 1) { + throw "The current matrix does not contain exactly one profile for PowerShell $CurrentVersion." + } + + $CandidateArchives = @($PatchUpdate.Archives) + if ($CandidateArchives.Count -ne @($Matrix.lanes).Count) { + throw "PowerShell $CandidateVersion does not have one official archive for every declared lane." + } + foreach ($Lane in @($Matrix.lanes)) { + $CandidateArchive = @($CandidateArchives | Where-Object { + $_.Platform -eq $Lane.platform -and $_.Architecture -eq $Lane.architecture + }) + if ($CandidateArchive.Count -ne 1 -or -not $CandidateArchive[0].Complete) { + throw "PowerShell $CandidateVersion lacks one complete official archive for $($Lane.platform)/$($Lane.architecture)." + } + $ExistingArchive = @($Matrix.archiveAssets | Where-Object { + $_.powerShellVersion -eq $CurrentVersion -and + $_.platform -eq $Lane.platform -and + $_.architecture -eq $Lane.architecture + }) + if ($ExistingArchive.Count -ne 1) { + throw "The current matrix lacks one archive row for PowerShell $CurrentVersion on $($Lane.platform)/$($Lane.architecture)." + } + $ExistingArchive[0].powerShellVersion = $CandidateVersion + $ExistingArchive[0].fileName = [string]$CandidateArchive[0].FileName + $ExistingArchive[0].sha256 = ([string]$CandidateArchive[0].Sha256).ToLowerInvariant() + $ExistingArchive[0].downloadUrl = [string]$CandidateArchive[0].DownloadUrl + } + + $DotNetRuntimeVersion = if ($IsPreparation) { $null } else { [string]$MatrixProfile[0].dotnetRuntimeVersion } + if (-not $IsPreparation) { + $CandidateIdentities = @($IdentityRecords | Where-Object { [string]$_.PowerShellVersion -eq $CandidateVersion }) + if ($CandidateIdentities.Count -ne @($Matrix.lanes).Count) { + throw "PowerShell $CandidateVersion requires $(@($Matrix.lanes).Count) runtime identities; found $($CandidateIdentities.Count)." + } + foreach ($Lane in @($Matrix.lanes)) { + $LaneIdentity = @($CandidateIdentities | Where-Object { + [string]$_.Platform -eq [string]$Lane.platform -and + [string]$_.Architecture -eq [string]$Lane.architecture + }) + if ($LaneIdentity.Count -ne 1) { + throw "PowerShell $CandidateVersion requires one runtime identity for $($Lane.platform)/$($Lane.architecture)." + } + if ([version]$LaneIdentity[0].PowerShellVersion -ne [version]$CandidateVersion) { + throw "A candidate runtime identity does not report the exact PowerShell patch $CandidateVersion." + } + if ([version]$LaneIdentity[0].DotNetVersion -lt [version]'1.0') { + throw "PowerShell $CandidateVersion reported an invalid .NET runtime identity." + } + } + $DotNetVersions = @($CandidateIdentities.DotNetVersion | ForEach-Object { ([version]$_).ToString() } | Sort-Object -Unique) + if ($DotNetVersions.Count -ne 1) { + throw "PowerShell $CandidateVersion archives disagree on the bundled .NET runtime: $($DotNetVersions -join ', ')." + } + $DotNetRuntimeVersion = [string]$DotNetVersions[0] + if (([version]$DotNetRuntimeVersion).Major -ne [int]$MatrixProfile[0].dotnetMajor) { + throw "PowerShell $CandidateVersion reports .NET $DotNetRuntimeVersion, outside declared CLR major $($MatrixProfile[0].dotnetMajor)." + } + } + + $MatrixProfile[0].powerShellVersion = $CandidateVersion + $MatrixProfile[0].dotnetRuntimeVersion = $DotNetRuntimeVersion + $UpdatedLines.Add([PSCustomObject]@{ + ReleaseLine = [string]$PatchUpdate.ReleaseLine + CurrentVersion = $CurrentVersion + CandidateVersion = $CandidateVersion + DotNetRuntimeVersion = if ($IsPreparation) { 'pending-runtime-identity' } else { $DotNetRuntimeVersion } + RuntimeIdentityStatus = if ($IsPreparation) { 'pending-all-lanes' } else { 'verified-all-lanes' } + }) +} + +if ($IsPreparation) { + $Matrix | Add-Member -NotePropertyName candidateValidationPending -NotePropertyValue $true -Force +} else { + $VerifiedTimestamp = $VerifiedAtUtc.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ', [System.Globalization.CultureInfo]::InvariantCulture) + $Matrix | Add-Member -NotePropertyName lastVerifiedUtc -NotePropertyValue $VerifiedTimestamp -Force + if ($Matrix.PSObject.Properties.Name -contains 'candidateValidationPending') { + $Matrix.PSObject.Properties.Remove('candidateValidationPending') + } +} + +$OutputDirectory = Split-Path -Path $OutputPath -Parent +if ($OutputDirectory -and -not (Test-Path -LiteralPath $OutputDirectory -PathType Container)) { + $null = New-Item -Path $OutputDirectory -ItemType Directory -Force +} +$Matrix | ConvertTo-Json -Depth 30 | Set-Content -LiteralPath $OutputPath -Encoding UTF8 + +[PSCustomObject]@{ + Mode = if ($IsPreparation) { 'CandidatePreparation' } else { 'VerifiedProposal' } + OutputPath = (Resolve-Path -LiteralPath $OutputPath).Path + UpdatedLines = @($UpdatedLines) +}