diff --git a/.pipelines/apiscan-gen-notice.yml b/.pipelines/apiscan-gen-notice.yml new file mode 100644 index 00000000000..02ab4ba3796 --- /dev/null +++ b/.pipelines/apiscan-gen-notice.yml @@ -0,0 +1,79 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +trigger: none + +variables: + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: CDP_DEFINITION_BUILD_COUNT + value: $[counter('', 0)] + # Defines the variables AzureFileCopySubscription, StorageAccount, StorageAccountKey, StorageResourceGroup, StorageSubscriptionName + - group: 'Azure Blob variable group' + # Defines the variables CgPat, CgOrganization, and CgProject + - group: 'ComponentGovernance' + - group: 'PoolNames' + - name: LinuxContainerImage + value: onebranch.azurecr.io/linux/ubuntu-2004:latest + - name: WindowsContainerImage + value: onebranch.azurecr.io/windows/ltsc2022/vse2022:latest + +resources: + repositories: + - repository: templates + type: git + name: OneBranch.Pipelines/GovernedTemplates + ref: refs/heads/main + +extends: + template: v2/OneBranch.NonOfficial.CrossPlat.yml@templates + parameters: + featureFlags: + WindowsHostVersion: + Version: 2022 + globalSdl: + compiled: + enabled: true + armory: + enabled: false + sbom: + enabled: false + cg: + enabled: true + ignoreDirectories: '.devcontainer,demos,docker,docs,src,test,tools/packaging' + tsa: + enabled: true # onebranch publish all SDL results to TSA. If TSA is disabled all SDL tools will forced into 'break' build mode. + credscan: + enabled: true + scanFolder: $(Build.SourcesDirectory) + suppressionsFile: $(Build.SourcesDirectory)\.config\suppress.json + binskim: + break: true # always break the build on binskim issues in addition to TSA upload + policheck: + break: true # always break the build on policheck issues. You can disable it by setting to 'false' + # APIScan requires a non-Ready-To-Run build + apiscan: + enabled: true + softwareName: "PowerShell" # Default is repo name + versionNumber: "7.5" # Default is build number + isLargeApp: false # Default: false. +#softwareFolder - relative path to a folder to be scanned. Default value is root of artifacts folder. +#symbolsFolder - relative path to a folder that contains symbols. Default value is root of artifacts folder. + + tsaOptionsFile: .config\tsaoptions.json + + stages: + - stage: APIScan + displayName: 'ApiScan' + dependsOn: [] + jobs: + - template: /.pipelines/templates/compliance/apiscan.yml@self + parameters: + parentJobs: [] + - stage: notice + displayName: Generate Notice File + dependsOn: [] + jobs: + - template: /.pipelines/templates/compliance/generateNotice.yml@self + parameters: + parentJobs: [] diff --git a/.pipelines/templates/compliance/apiscan.yml b/.pipelines/templates/compliance/apiscan.yml new file mode 100644 index 00000000000..a96471aecd9 --- /dev/null +++ b/.pipelines/templates/compliance/apiscan.yml @@ -0,0 +1,181 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +jobs: + - job: APIScan + variables: + - name: runCodesignValidationInjection + value : false + - name: NugetSecurityAnalysisWarningLevel + value: none + - name: ReleaseTagVar + value: fromBranch + # Defines the variables APIScanClient, APIScanTenant and APIScanSecret + - group: PS-PS-APIScan + # PAT permissions NOTE: Declare a SymbolServerPAT variable in this group with a 'microsoft' organizanization scoped PAT with 'Symbols' Read permission. + # A PAT in the wrong org will give a single Error 203. No PAT will give a single Error 401, and individual pdbs may be missing even if permissions are correct. + - group: symbols + - name: branchCounterKey + value: $[format('{0:yyyyMMdd}-{1}', pipeline.startTime,variables['Build.SourceBranch'])] + - name: branchCounter + value: $[counter(variables['branchCounterKey'], 1)] + - group: DotNetPrivateBuildAccess + - group: Azure Blob variable group + - group: ReleasePipelineSecrets + - group: mscodehub-feed-read-general + - group: mscodehub-feed-read-akv + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: repoRoot + value: '$(Build.SourcesDirectory)\PowerShell' + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + + pool: + type: windows + + # APIScan can take a long time + timeoutInMinutes: 180 + + steps: + - checkout: self + clean: true + fetchTags: true + fetchDepth: 1000 + displayName: Checkout PowerShell + retryCountOnTaskFailure: 1 + env: + ob_restore_phase: true # This ensures checkout is done at the beginning of the restore phase + + - template: ../SetVersionVariables.yml + parameters: + ReleaseTagVar: $(ReleaseTagVar) + CreateJson: yes + UseJson: no + + - template: ../insert-nuget-config-azfeed.yml + parameters: + repoRoot: '$(repoRoot)' + + - pwsh: | + Import-Module .\build.psm1 -force + Start-PSBootstrap + workingDirectory: '$(repoRoot)' + retryCountOnTaskFailure: 2 + displayName: 'Bootstrap' + env: + __DOTNET_RUNTIME_FEED_KEY: $(RUNTIME_SOURCEFEED_KEY) + + - pwsh: | + Import-Module .\build.psm1 -force + Find-DotNet + dotnet tool install dotnet-symbol --tool-path $(Agent.ToolsDirectory)\tools\dotnet-symbol + $symbolToolPath = Get-ChildItem -Path $(Agent.ToolsDirectory)\tools\dotnet-symbol\dotnet-symbol.exe | Select-Object -First 1 -ExpandProperty FullName + Write-Host "##vso[task.setvariable variable=symbolToolPath]$symbolToolPath" + displayName: Install dotnet-symbol + workingDirectory: '$(repoRoot)' + retryCountOnTaskFailure: 2 + + - pwsh: | + $modules = 'Az.Accounts', 'Az.Storage' + foreach($module in $modules) { + if(!(get-module $module -listavailable)) { + Write-Verbose "installing $module..." -verbose + Install-Module $module -force -AllowClobber + } else { + Write-Verbose "$module already installed." -verbose + } + } + displayName: Install PowerShell modules + workingDirectory: '$(repoRoot)' + + - task: AzurePowerShell@5 + displayName: Download winverify-private Artifacts + inputs: + azureSubscription: az-blob-cicd-infra + scriptType: inlineScript + azurePowerShellVersion: LatestVersion + workingDirectory: '$(repoRoot)' + pwsh: true + inline: | + # download smybols for getfilesiginforedist.dll + $downloadsDirectory = '$(Build.ArtifactStagingDirectory)/downloads' + $uploadedDirectory = '$(Build.ArtifactStagingDirectory)/uploaded' + $storageAccountName = "pscoretestdata" + $containerName = 'winverify-private' + $winverifySymbolsPath = New-Item -ItemType Directory -Path '$(System.ArtifactsDirectory)/winverify-symbols' -Force + $dllName = 'getfilesiginforedist.dll' + $winverifySymbolsDllPath = Join-Path $winverifySymbolsPath $dllName + + $context = New-AzStorageContext -StorageAccountName $storageAccountName -UseConnectedAccount + + Get-AzStorageBlobContent -Container $containerName -Blob $dllName -Destination $winverifySymbolsDllPath -Context $context + + - pwsh: | + Get-ChildItem -Path '$(System.ArtifactsDirectory)/winverify-symbols' + displayName: Capture winverify-private Artifacts + workingDirectory: '$(repoRoot)' + condition: succeededOrFailed() + + - pwsh: | + Import-Module .\build.psm1 -force + Find-DotNet + Start-PSBuild -Configuration StaticAnalysis -PSModuleRestore -Clean -Runtime fxdependent-win-desktop + + $OutputFolder = Split-Path (Get-PSOutput) + + Write-Verbose -Verbose -Message "Deleting ref folder from output folder" + if (Test-Path $OutputFolder/ref) { + Remove-Item -Recurse -Force $OutputFolder/ref + } + + Copy-Item -Path "$OutputFolder\*" -Destination '$(ob_outputDirectory)' -Recurse -Verbose + + workingDirectory: '$(repoRoot)' + displayName: 'Build PowerShell Source' + + - pwsh: | + Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + workingDirectory: '$(repoRoot)' + displayName: Capture Environment + condition: succeededOrFailed() + + # Explicitly download symbols for the drop since the SDL image doesn't have http://SymWeb access and APIScan cannot handle https yet. + - pwsh: | + Import-Module .\build.psm1 -force + Find-DotNet + $pat = '$(SymbolServerPAT)' + if ($pat -like '*PAT*' -or $pat -eq '') + { + throw 'No PAT defined' + } + $url = 'https://microsoft.artifacts.visualstudio.com/defaultcollection/_apis/symbol/symsrv' + $(symbolToolPath) --authenticated-server-path $(SymbolServerPAT) $url --symbols -d "$env:ob_outputDirectory\*" --recurse-subdirectories + displayName: 'Download Symbols for binaries' + retryCountOnTaskFailure: 2 + workingDirectory: '$(repoRoot)' + + - pwsh: | + Get-ChildItem '$(ob_outputDirectory)' -File -Recurse | + Foreach-Object { + [pscustomobject]@{ + Path = $_.FullName + Version = $_.VersionInfo.FileVersion + Md5Hash = (Get-FileHash -Algorithm MD5 -Path $_.FullName).Hash + Sha512Hash = (Get-FileHash -Algorithm SHA512 -Path $_.FullName).Hash + } + } | Export-Csv -Path '$(Build.SourcesDirectory)/ReleaseFileHash.csv' + workingDirectory: '$(repoRoot)' + displayName: 'Create release file hash artifact' + + - pwsh: | + Copy-Item -Path '$(Build.SourcesDirectory)/ReleaseFileHash.csv' -Destination '$(ob_outputDirectory)' -Verbose + displayName: 'Publish Build File Hash artifact' + + - pwsh: | + Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: Capture Environment + condition: succeededOrFailed() + workingDirectory: '$(repoRoot)' diff --git a/.pipelines/templates/compliance/generateNotice.yml b/.pipelines/templates/compliance/generateNotice.yml new file mode 100644 index 00000000000..0c1282ea8ce --- /dev/null +++ b/.pipelines/templates/compliance/generateNotice.yml @@ -0,0 +1,154 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +parameters: + - name: parentJobs + type: jobList + +jobs: +- job: generateNotice + variables: + - name: runCodesignValidationInjection + value : false + - name: NugetSecurityAnalysisWarningLevel + value: none + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT/notice' + - name: ob_sdl_apiscan_enabled + value: false + - name: repoRoot + value: '$(Build.SourcesDirectory)\PowerShell' + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + + displayName: Generate Notice + dependsOn: + ${{ parameters.parentJobs }} + pool: + type: windows + + timeoutInMinutes: 15 + + steps: + - checkout: self + clean: true + + - pwsh: | + [string]$Branch=$env:BUILD_SOURCEBRANCH + $branchOnly = $Branch -replace '^refs/heads/'; + $branchOnly = $branchOnly -replace '[_\-]' + + if ($branchOnly -eq 'master') { + $container = 'tpn' + } else { + $branchOnly = $branchOnly -replace '[\./]', '-' + $container = "tpn-$branchOnly" + } + + $vstsCommandString = "vso[task.setvariable variable=tpnContainer]$container" + Write-Verbose -Message $vstsCommandString -Verbose + Write-Host -Object "##$vstsCommandString" + displayName: Set ContainerName + + - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: 'Component Detection' + inputs: + sourceScanPath: '$(repoRoot)\tools' + + - pwsh: | + $(repoRoot)/tools/clearlyDefined/ClearlyDefined.ps1 -TestAndHarvest + displayName: Verify that packages have license data + + + - task: msospo.ospo-extension.8d7f9abb-6896-461d-9e25-4f74ed65ddb2.notice@0 + displayName: 'NOTICE File Generator' + inputs: + outputfile: '$(ob_outputDirectory)\ThirdPartyNotices.txt' + # output format can be html or text + outputformat: text + # this isn't working + # additionaldata: $(Build.SourcesDirectory)\assets\additionalAttributions.txt + + + - pwsh: | + Get-Content -Raw -Path $(repoRoot)\assets\additionalAttributions.txt | Out-File '$(ob_outputDirectory)\ThirdPartyNotices.txt' -Encoding utf8NoBOM -Force -Append + Get-Content -Raw -Path $(repoRoot)\assets\additionalAttributions.txt + displayName: Append Additional Attributions + continueOnError: true + + - pwsh: | + Get-Content -Raw -Path '$(ob_outputDirectory)\ThirdPartyNotices.txt' + displayName: Capture Notice + continueOnError: true + + - powershell: | + [System.Net.ServicePointManager]::SecurityProtocol = + [System.Net.ServicePointManager]::SecurityProtocol -bor + [System.Security.Authentication.SslProtocols]::Tls12 -bor + [System.Security.Authentication.SslProtocols]::Tls11 + + Set-ItemProperty -Path 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NetFramework\v4.0.30319' -Name 'SchUseStrongCrypto' -Value '1' -Type DWord + Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\.NetFramework\v4.0.30319' -Name 'SchUseStrongCrypto' -Value '1' -Type DWord + Get-PackageProvider -Name NuGet -ForceBootstrap + displayName: Initalize PowerShellGet + + - powershell: | + $modules = 'Az.Accounts', 'Az.Storage' + foreach($module in $modules) { + if(!(get-module $module -listavailable)) { + Write-Verbose "installing $module..." -verbose + Install-Module $module -force -AllowClobber + } else { + Write-Verbose "$module already installed." -verbose + #Update-Module $module -verbose + } + } + displayName: Install PowerShell modules + + - powershell: | + if(Get-Command -Name Uninstall-AzureRm -ErrorAction Ignore){ + Write-Verbose "running Uninstall-AzureRm" -verbose + Uninstall-AzureRm + } else { + Write-Verbose "Uninstall-AzureRm not present" -verbose + } + displayName: Uninstall Uninstall-AzureRm + continueOnError: true + + - task: AzurePowerShell@5 + displayName: Upload Notice + inputs: + azureSubscription: az-blob-cicd-infra + scriptType: inlineScript + azurePowerShellVersion: LatestVersion + workingDirectory: '$(repoRoot)' + pwsh: true + inline: | + try { + $downloadsDirectory = '$(Build.ArtifactStagingDirectory)/downloads' + $uploadedDirectory = '$(Build.ArtifactStagingDirectory)/uploaded' + $storageAccountName = "pscoretestdata" + $containerName = '$(tpnContainer)' + $blobName = 'ThirdPartyNotices.txt' + $noticePath = "$(ob_outputDirectory)\$blobName" + + Write-Verbose -Verbose "creating context ($storageAccountName) ..." + $context = New-AzStorageContext -StorageAccountName $storageAccountName -UseConnectedAccount + + Write-Verbose -Verbose "checking if container ($containerName) exists ..." + $containerExists = Get-AzStorageContainer -Name $containerName -Context $context -ErrorAction SilentlyContinue + if (-not $containerExists) { + Write-Verbose -Verbose "Creating container ..." + $null = New-AzStorageContainer -Name $containerName -Context $context + Write-Verbose -Verbose "Blob container $containerName created successfully." + } + + Write-Verbose -Verbose "Setting blob ($blobName) content ($noticePath) ..." + $null = Set-AzStorageBlobContent -File $noticePath -Container $containerName -Blob $blobName -Context $context -confirm:$false -force + Write-Verbose -Verbose "Done" + } catch { + Get-Error + throw + }