Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 21 additions & 12 deletions OneBranchPipelines/build-release-package-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ parameters:
default: 'NonOfficial'

# Enable/disable SDL security tasks (BinSkim, CredScan, PoliCheck, etc.)
# Set to false for faster builds during development
# NonOfficial builds may disable these for development; Official builds always run them.
- name: runSdlTasks
displayName: 'Run SDL Security Tasks'
type: boolean
Expand Down Expand Up @@ -281,7 +281,7 @@ extends:
# Checks for known vulnerabilities in compiled artifacts
# break:true = fail build if critical issues found
armory:
enabled: ${{ parameters.runSdlTasks }}
enabled: ${{ or(eq(variables.effectiveOneBranchType, 'Official'), parameters.runSdlTasks) }}
break: true

# AsyncSdl - Asynchronous SDL tasks (run after build completion)
Expand All @@ -297,18 +297,18 @@ extends:
# - Control Flow Guard (CFG)
# Scans: .pyd (Python), .dll/.exe (Windows), .so (Linux), .dylib (macOS)
binskim:
enabled: ${{ parameters.runSdlTasks }}
enabled: ${{ or(eq(variables.effectiveOneBranchType, 'Official'), parameters.runSdlTasks) }}
break: true # Fail build on critical BinSkim errors
# Recursive scan of all binary file types
analyzeTarget: '$(Build.SourcesDirectory)/**/*.{pyd,dll,exe,so,dylib}'
analyzeTarget: '$(Build.SourcesDirectory)/**/*.{pyd,dll,exe,so,so.*,dylib,rll}'
analyzeRecurse: true
# SARIF output (Static Analysis Results Interchange Format)
logFile: '$(Build.ArtifactStagingDirectory)/BinSkimResults.sarif'

# CodeInspector - Source code security analysis
# Checks Python/C++ code for security anti-patterns
codeinspector:
enabled: ${{ parameters.runSdlTasks }}
enabled: ${{ or(eq(variables.effectiveOneBranchType, 'Official'), parameters.runSdlTasks) }}
logLevel: Error

# CodeQL - Semantic code analysis (GitHub Advanced Security)
Expand All @@ -319,7 +319,7 @@ extends:
# - Integer overflows
# security-extended suite = comprehensive security queries
codeql:
enabled: ${{ parameters.runSdlTasks }}
enabled: ${{ or(eq(variables.effectiveOneBranchType, 'Official'), parameters.runSdlTasks) }}
language: 'python,cpp'
sourceRoot: '$(REPO_ROOT)'
querySuite: security-extended
Expand All @@ -328,7 +328,7 @@ extends:
# Detects hardcoded credentials, API keys, passwords in code
# Uses global baseline/suppression files configured above
credscan:
enabled: ${{ parameters.runSdlTasks }}
enabled: ${{ or(eq(variables.effectiveOneBranchType, 'Official'), parameters.runSdlTasks) }}

# ESLint - JavaScript/TypeScript linter
# Disabled: Not applicable to Python/C++ project
Expand All @@ -339,7 +339,7 @@ extends:
# Scans code and documentation for inappropriate terms
# Exclusion file contains approved exceptions (technical terms)
policheck:
enabled: ${{ parameters.runSdlTasks }}
enabled: ${{ or(eq(variables.effectiveOneBranchType, 'Official'), parameters.runSdlTasks) }}
break: true
exclusionFile: '$(REPO_ROOT)/.config/PolicheckExclusions.xml'

Expand All @@ -352,15 +352,15 @@ extends:
# Uploads security scan results (SARIF files) to pipeline artifacts
# Used for audit trail and compliance reporting
publishLogs:
enabled: ${{ parameters.runSdlTasks }}
enabled: ${{ or(eq(variables.effectiveOneBranchType, 'Official'), parameters.runSdlTasks) }}

# SBOM - Software Bill of Materials
# Generates machine-readable list of all dependencies
# Required for supply chain security and compliance
# Format: SPDX or CycloneDX
# Version automatically detected from wheel metadata (setup.py)
sbom:
enabled: ${{ parameters.runSdlTasks }}
enabled: ${{ or(eq(variables.effectiveOneBranchType, 'Official'), parameters.runSdlTasks) }}
# This pipeline always builds both packages; name the SBOM after the primary
# mssql-python wheel set.
packageName: 'mssql-python'
Expand All @@ -369,7 +369,7 @@ extends:
# Uploads scan results to Microsoft's TSA tool for tracking
# Only enabled for Official builds (production compliance requirement)
tsa:
enabled: ${{ and(eq(variables.effectiveOneBranchType, 'Official'), parameters.runSdlTasks) }}
enabled: ${{ eq(variables.effectiveOneBranchType, 'Official') }}
configFile: '$(REPO_ROOT)/.config/tsaoptions.json'

# =========================
Expand Down Expand Up @@ -486,7 +486,7 @@ extends:
# - dist/bindings/Windows/*.{pyd,pdb} (Windows native extensions)
# - dist/bindings/macOS/*.so (macOS universal2 binaries)
# - dist/bindings/Linux/*.so (Linux native extensions)
# This stage also runs final BinSkim scan on all binaries
# ScanWheelBinaries scans the consolidated wheel payloads on a Windows host.
- stage: Consolidate
displayName: 'Consolidate All Artifacts'
dependsOn:
Expand Down Expand Up @@ -566,3 +566,12 @@ extends:
# mssql-python build stages now install the external mssql-python-odbc wheel
# (from ConsolidateOdbc) and run the full pytest suite against it — so the
# external-package resolution is already validated end-to-end during the build.

- ${{ if or(eq(variables.effectiveOneBranchType, 'Official'), parameters.runSdlTasks) }}:
- stage: ScanWheelBinaries
displayName: 'Validate binary scan coverage'
dependsOn:
- Consolidate
- ConsolidateOdbc
jobs:
- template: /OneBranchPipelines/jobs/scan-wheel-binaries-job.yml@self
2 changes: 1 addition & 1 deletion OneBranchPipelines/jobs/consolidate-artifacts-job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
vmImage: 'ubuntu-latest'

variables:
# Disable BinSkim - consolidation job only downloads artifacts, no binary builds
# Wheel payloads are scanned by the downstream ScanWheelBinaries stage.
- name: ob_sdl_binskim_enabled
value: false
- name: ob_outputDirectory
Expand Down
2 changes: 1 addition & 1 deletion OneBranchPipelines/jobs/consolidate-odbc-artifacts-job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
vmImage: 'ubuntu-latest'

variables:
# Consolidation only moves files; no binaries to scan.
# Wheel payloads are scanned by the downstream ScanWheelBinaries stage.
- name: ob_sdl_binskim_enabled
value: false
- name: ob_outputDirectory
Expand Down
28 changes: 28 additions & 0 deletions OneBranchPipelines/jobs/scan-wheel-binaries-job.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
jobs:
- job: ScanWheelBinaries
displayName: 'Scan all built wheel payloads'
pool:
type: windows
isCustom: true
name: Python-1ES-pool
demands:
- imageOverride -equals PYTHON-1ES-MMS2022
variables:
ob_outputDirectory: '$(Build.ArtifactStagingDirectory)'
steps:
- checkout: self
fetchDepth: 1
- task: DownloadPipelineArtifact@2
displayName: 'Download both consolidated wheel packages'
inputs:
buildType: 'current'
itemPattern: |
drop_Consolidate_ConsolidateArtifacts/dist/*.whl
drop_Consolidate_ConsolidateArtifacts/symbols/**
drop_ConsolidateOdbc_ConsolidateArtifacts/dist/*.whl
targetPath: '$(Pipeline.Workspace)/wheels-to-scan'
- template: /OneBranchPipelines/steps/scan-wheel-binaries-step.yml@self
parameters:
wheelDirectory: '$(Pipeline.Workspace)/wheels-to-scan'
expectedWheelCount: 41
symbolDirectory: '$(Pipeline.Workspace)/wheels-to-scan'
9 changes: 9 additions & 0 deletions OneBranchPipelines/official-release-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,15 @@ extends:

Write-Host "`nAll wheels verified successfully!"

- template: /OneBranchPipelines/steps/scan-wheel-binaries-step.yml@self
parameters:
wheelDirectory: '$(Build.SourcesDirectory)/artifacts/dist'
symbolDirectory: '$(Build.SourcesDirectory)/artifacts/symbols'
${{ if eq(parameters.releasePackage, 'mssql-python') }}:
expectedWheelCount: 34
${{ else }}:
expectedWheelCount: 7

# Step 5: Publish Symbols (mssql-python only; mssql-python-odbc has no PDBs)
- ${{ if and(eq(parameters.publishSymbols, true), eq(parameters.releasePackage, 'mssql-python')) }}:
- template: /OneBranchPipelines/steps/symbol-publishing-step.yml@self
Expand Down
12 changes: 9 additions & 3 deletions OneBranchPipelines/stages/build-linux-single-stage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ stages:
timeoutInMinutes: 120

variables:
# Disable BinSkim for Linux - requires ICU libraries not available in manylinux/musllinux containers
# Wheel payloads are scanned on the Windows ScanWheelBinaries stage.
- name: ob_sdl_binskim_enabled
value: false
# OneBranch output directory for artifacts (wheels, bindings, symbols)
Expand Down Expand Up @@ -294,6 +294,10 @@ stages:
cd /workspace;
python setup.py bdist_wheel;

# Check build tooling from the checkout before wheel-only isolation.
python -m pip install -q pytest;
python -m pytest --noconftest tests/test_037_native_build_checks.py -v;

# Step 5: Install wheel in isolated directory for testing
echo "Installing wheel in isolated test environment...";
TEST_DIR="/test_isolated_${PYBIN}";
Expand All @@ -316,7 +320,6 @@ stages:

# Step 7: Setup test environment
echo "Setting up test environment...";
$PY -m pip install -q pytest;
cp -r /workspace/tests $TEST_DIR/ || echo "WARNING: No tests directory";
# Some tests read repo-side helper scripts/workflows (e.g. .github/scripts/prepare_fork_coverage_comment.py).
cp -r /workspace/.github $TEST_DIR/ || echo "WARNING: No .github directory";
Expand Down Expand Up @@ -364,6 +367,10 @@ stages:
cd /workspace;
python setup.py bdist_wheel;

# Check build tooling from the checkout before wheel-only isolation.
python -m pip install -q pytest;
python -m pytest --noconftest tests/test_037_native_build_checks.py -v;

# Step 5: Install wheel in isolated directory for testing
echo "Installing wheel in isolated test environment...";
TEST_DIR="/test_isolated_${PYBIN}";
Expand All @@ -386,7 +393,6 @@ stages:

# Step 7: Setup test environment
echo "Setting up test environment...";
$PY -m pip install -q pytest;
cp -r /workspace/tests $TEST_DIR/ || echo "WARNING: No tests directory";
# Some tests read repo-side helper scripts/workflows (e.g. .github/scripts/prepare_fork_coverage_comment.py).
cp -r /workspace/.github $TEST_DIR/ || echo "WARNING: No .github directory";
Expand Down
2 changes: 1 addition & 1 deletion OneBranchPipelines/stages/build-macos-single-stage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ stages:

# Build Variables
variables:
# Disable BinSkim (Windows-focused binary analyzer) - macOS uses Mach-O format, not PE
# Wheel payloads are scanned on the Windows ScanWheelBinaries stage.
- name: ob_sdl_binskim_enabled
value: false
# OneBranch artifact output directory
Expand Down
44 changes: 44 additions & 0 deletions OneBranchPipelines/steps/scan-wheel-binaries-step.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
parameters:
- name: wheelDirectory
type: string
- name: expectedWheelCount
type: number
- name: symbolDirectory
type: string

steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.13'
architecture: 'x64'
displayName: 'Select Python for binary inventory'

- task: PowerShell@2
displayName: 'Scan built wheel payloads'
inputs:
targetType: 'inline'
script: |
$ErrorActionPreference = 'Stop'
$toolRoot = "$(Build.BinariesDirectory)/binskim-4.4.9.11-$(Build.BuildId)-$(System.JobAttempt)"
New-Item -ItemType Directory -Path $toolRoot | Out-Null
$package = "$toolRoot/package.zip"
Invoke-WebRequest -UseBasicParsing -Uri "https://www.nuget.org/api/v2/package/Microsoft.CodeAnalysis.BinSkim/4.4.9.11" -OutFile $package
$expected = "69678989cbc273b5b50fcf98fb0fd978e1e35a3f844acb24254b31f0ce90c447"
if ((Get-FileHash $package -Algorithm SHA256).Hash.ToLowerInvariant() -ne $expected) {
throw "BinSkim package digest mismatch"
}
Expand-Archive -Path $package -DestinationPath "$toolRoot/tool"
$scanner = "$toolRoot/tool/tools/net9.0/win-x64/BinSkim.exe"
& $scanner --version
if ($LASTEXITCODE -ne 0) { throw "BinSkim could not start" }
python "$(Build.SourcesDirectory)/eng/scripts/scan_wheel_binaries.py" `
"${{ parameters.wheelDirectory }}" "$(Build.BinariesDirectory)/wheel-scan-$(Build.BuildId)-$(System.JobAttempt)" $scanner `
--expected-wheels ${{ parameters.expectedWheelCount }} --symbols "${{ parameters.symbolDirectory }}"
if ($LASTEXITCODE -ne 0) { throw "Wheel binary validation failed" }

- task: PublishPipelineArtifact@1
displayName: 'Publish binary scan reports'
condition: succeededOrFailed()
inputs:
targetPath: '$(Build.BinariesDirectory)/wheel-scan-$(Build.BuildId)-$(System.JobAttempt)/reports'
artifact: 'wheel-binary-scan-$(System.StageName)-$(System.JobName)'
Loading