Skip to content

Enable SDL Roslyn analysis (incl. internal IA* analyzers) in the OneBranch build - #4464

Open
paulmedynski wants to merge 16 commits into
mainfrom
dev/paul/onebranch-2025-roslyn
Open

Enable SDL Roslyn analysis (incl. internal IA* analyzers) in the OneBranch build#4464
paulmedynski wants to merge 16 commits into
mainfrom
dev/paul/onebranch-2025-roslyn

Conversation

@paulmedynski

@paulmedynski paulmedynski commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Description

Adds Roslyn analysis to our C# projects so it can be explicitly performed as part of Official builds. Previously, we were relying entirely on an internal Azure Pipelines task (RoslynAnalyzers@3) which was silently failing to perform any analysis.

Roslyn analysis via "Copy Logs Only"

.NET Roslyn analyzers are compiler-integrated — they only run during the real csc compilation. build.proj is an orchestrator that compiles each package in a separate child dotnet build via <Exec>, so analyzer properties injected onto the outer command never cross into the real compiles. That is why the earlier auto/manual-mode attempts collected 0 SARIF. Instead, the analyzers are baked into the leaf builds and the RoslynAnalyzers@3 task runs in Copy Logs Only mode:

  • build.proj gains an EnableAnalyzers flag that is forwarded into every leaf dotnet build.
  • src/Directory.Build.props reacts by enabling the analyzer set (AnalysisLevel=latest-recommended; IDE code-style rules stay off via EnforceCodeStyleInBuild=false) and emitting one SARIF v1 log per project. ErrorLog intentionally omits ,version=2, because the Guardian sanitizer deserializes SARIF v1 (a v2 log throws and is dropped).
  • The analysis pass sets TreatWarningsAsErrors=false, overriding the repo-wide true. Warning-clean compilation is still enforced — every job runs a full ordinary build later with TreatWarningsAsErrors=true — so promoting analyzer warnings to errors here adds no coverage. It only fails the analysis build and leaves the step orange. This also matches what the Guardian RoslynAnalyzers task does in its own build-driving modes (/p:TreatWarningsAsErrors=false).
  • The step runs an isolated analysis build (IsolatedBuildPath redirects each project's bin and forces a full recompile) so it never disturbs real build output, then the task only collects/sanitizes the *.csproj.*.sarif logs. Because Copy Logs Only performs no build, it needs no MSBuild and is agnostic to the container's VS/MSBuild version. A guard step fails fast if a package produced no SARIF.

Internal IA* analyzers (private ADO.Net pipeline only)

AnalysisLevel=latest-recommended covers the CA* rules but not the internal Microsoft.Internal.Analyzers ("IA*") rules. That package is Microsoft-internal and confidential, so:

  • It is referenced only when InternalAnalyzers=true (gated in Directory.Build.props / Directory.Packages.props, PrivateAssets=all, so it never ships).
  • The step generates a private NuGet.config at runtime pointing at the internal feed, with packageSourceMapping scoping Microsoft.Internal.* to that feed so the confidential package never touches the public governed feed.
  • The feed URL and pinned version come from the internal-analyzers-variables-v1 variable group, defined only in the private ADO.Net project. No public/private gate is needed because the OneBranch pipelines run only in that project.

SDL baseline

  • .config/guardian/.gdnbaselines holds 56 entries: 48 generated by the analysis run (47 from the six package SDL artifacts, 1 from the SDL sources stage) plus 9 non-Roslyn entries carried over from main (1 overlapping).
  • Regenerated from 26240.2, the first run with complete Roslyn coverage. The earlier 835-entry baseline was captured under AnalysisLevel=latest-all with EnforceCodeStyleInBuild=true (587 CA + 234 IDE + 5 SYSLIB); 781 of those entries are no longer produced now that the pass runs latest-recommended with IDE rules off. Verified reproducible across builds 170470 and 170697.
  • What remains is 44 real Roslyn findings across four globalisation/correctness rules — CA1305 ×33, CA1309 ×10, CA1304 ×3, CA2219 ×1 — plus the non-Roslyn carry-overs. Note the baseline is coupled to the current analyzer configuration: raising AnalysisLevel back to latest-all, or re-enabling IDE rules, would require recapture.
  • Burn-down tracked in ADO.Net Feature #46588 with Roslyn-specific child User Story #46614.

Guardian break status: 1ES auto-baselining currently suppresses Roslyn

Roslyn findings cannot fail a build on this branch, regardless of breakOnSdlError. This is 1ES behaviour, not a gap in this PR, and it is expected to resolve itself once merged.

1ES onboarded this pipeline to auto-baselining between 2026-08-05 and 2026-08-27. In SDLBinaryAnalysis.yml, when the autoBaseline path is active — the default — five tools including roslyn have their break flag rebound from our parameter to the runtime variable $(OneES_AutoBaseline_roslyn_break). The pre-check task sets that to False for any tool with no 1ES baseline record, which prints Your pipeline is not yet baselined ... running SDL tools in no-break mode. Roslyn therefore never appears in Guardian Break's tool filter and its results are counted as excluded by tool filters, before baseline matching happens.

Confirmed by run logs: roslynEnabled = True, roslynBreak = True (our setting does flow through), then No record for retail.binary.roslyn so will require a new baseline. ApiScan still breaks because it is not one of the five rebound tools.

Self-baselining on this branch is not possible: the criteria require a refs/pull/ branch with no yaml or .gdnbaselines changes, which this PR cannot satisfy by construction. featureFlags.autoBaseline: false is also ineffective — OneBranch's Core.Template.yml hardcodes autoBaseline: true when the value is falsy.

Expected post-merge behaviour: running on the default branch lets 1ES generate its baseline record, after which sdl.roslyn.break is honoured again and this baseline gates the build — as it demonstrably did on 2026-08-05 (26217.2: Baselined results: 9 / 5 / 11, Active: 0 / 1 / 0). One caveat: when auto-baselining is fully active, GdnBreakBaselineFiles switches to the 1ES-generated baseline, so whether the committed file continues to be used or is superseded still needs confirming on the first default-branch run.

Windows Server 2025 build image

  • Upgraded the Windows build container to ltsc2025/vse2026 and set WindowsHostVersion: { Version: 2025 } so the host and container OS versions match.
  • Documented the host-vs-container relationship (and why LinuxHostVersion is not set — Linux containers share the host kernel), plus the legacy CDPx PAT flag (EnableCDPxPAT: false).

Testing

OneBranch builds - to confirm Roslyn behaviour:

  • sqlclient-non-official: 26203.5 — analyzers run across all packages, SARIF produced, per-package baselines generated. IA* verified loaded (IA5352 in the rule catalog; 0 active findings).
  • sqlclient-non-official: 26203.6 — committed .gdnbaselines loaded by Guardian Post-Analysis.
  • sqlclient-non-official: 26204.3 - break=true, and it failed with non-suppressable APIScan errors, as expected.
  • sqlclient-non-official: 26239.1break=false. SARIF collected from all 6 packages; analysis builds failed on promoted warnings (TreatWarningsAsErrors=true at the time).
  • sqlclient-non-official: 26240.1break=true. Failed on the known non-suppressable ApiScan finding; revealed that Roslyn is excluded from Guardian Break by 1ES auto-baselining.
  • sqlclient-non-official: 26240.2succeeded, fully green. All 6 analysis builds clean, SqlClient SARIF coverage 3 → 15 logs. Source of the regenerated baseline.
  • sqlclient-non-official: 26240.3succeeded, fully green. Validates the regenerated baseline and the explicit versioning fix: the analysis build now passes -p:BuildNumber=26240 -p:PackageVersionSqlClient=7.1.0-preview3.26240.3 with zero NU diagnostics, SARIF coverage is unchanged at 22 logs, and all 48 generated signatures are covered by the committed baseline (0 new findings).

PR/CI builds - to confirm that NuGet.config changes are benign:

  • The PR and CI runs are linked in the checks section.

Copilot AI lite review requested due to automatic review settings July 21, 2026 15:34
@github-project-automation github-project-automation Bot moved this to To triage in SqlClient Board Jul 21, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates OneBranch pipeline configuration to modernize the Windows build environment to Windows Server 2025 / VS 2026 containers and to make Roslyn Analyzers SDL run reliably on the new image by switching the task to auto-detected build replay.

Changes:

  • Upgraded the OneBranch Windows container image to ltsc2025/vse2026.
  • Set WindowsHostVersion: { Version: 2025 } (and documented host-vs-container expectations) in both official and non-official OneBranch pipelines.
  • Switched Roslyn Analyzers to auto mode and added a preceding “throwaway” dotnet build so the task can detect/replay compilation from logs; removed the now-unused packageVersion pass-through to that analyzer template.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
eng/pipelines/onebranch/variables/onebranch-variables.yml Bumps Windows container image to ltsc2025/vse2026 and adds a doc link for OneBranch images.
eng/pipelines/onebranch/steps/roslyn-analyzers-buildproj-step.yml Moves Roslyn analyzers to auto-detected build replay with a preceding DotNetCoreCLI@2 build for log generation; drops unused packageVersion parameter.
eng/pipelines/onebranch/sqlclient-official.yml Sets WindowsHostVersion to 2025 and adds detailed documentation about host/container relationship and CDPx PAT injection.
eng/pipelines/onebranch/sqlclient-non-official.yml Same host-version and documentation updates as the official pipeline.
eng/pipelines/onebranch/jobs/build-buildproj-job.yml Removes passing packageVersion into the roslyn analyzers step template (since it no longer accepts/needs it).

Comment thread eng/pipelines/onebranch/steps/roslyn-analyzers-buildproj-step.yml
@paulmedynski paulmedynski added the Area\Engineering Use this for issues that are targeted for changes in the 'eng' folder or build systems. label Jul 21, 2026
@paulmedynski paulmedynski added this to the 7.1.0-preview3 milestone Jul 21, 2026
@paulmedynski paulmedynski moved this from To triage to In progress in SqlClient Board Jul 21, 2026
parameters:
dependencyArguments: $(dependencyPackageArguments)
packageShortName: ${{ parameters.packageShortName }}
packageVersion: ${{ parameters.packageVersion }}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Roslyn template no longer needs this value.

Comment thread eng/pipelines/onebranch/steps/roslyn-analyzers-buildproj-step.yml
# Windows jobs use this image.
- name: WindowsContainerImage
value: onebranch.azurecr.io/windows/ltsc2022/vse2022:latest
value: onebranch.azurecr.io/windows/ltsc2025/vse2026:latest

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enable the latest tooling and environment for Windows jobs.

Copilot AI review requested due to automatic review settings July 21, 2026 18:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

eng/pipelines/onebranch/steps/roslyn-analyzers-buildproj-step.yml:98

  • This comment refers to the OAuth token env var as system_accesstoken, but the task is actually configured with the standard SYSTEM_ACCESSTOKEN name right below. Updating the comment avoids confusion when troubleshooting log-access failures.
  # analyzers. Requires OAuth token access (system_accesstoken) to read the build logs.

Comment thread eng/pipelines/onebranch/steps/roslyn-analyzers-buildproj-step.yml Outdated
Comment thread build.proj Outdated
@codecov

codecov Bot commented Jul 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 63.08%. Comparing base (ee6fabd) to head (0faf7e8).
⚠️ Report is 7 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4464      +/-   ##
==========================================
- Coverage   65.23%   63.08%   -2.16%     
==========================================
  Files         288      283       -5     
  Lines       44587    67609   +23022     
==========================================
+ Hits        29087    42651   +13564     
- Misses      15500    24958    +9458     
Flag Coverage Δ
CI-SqlClient ?
PR-SqlClient-Project 63.08% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI review requested due to automatic review settings July 22, 2026 10:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Comment thread eng/pipelines/onebranch/steps/roslyn-analyzers-buildproj-step.yml Outdated
Copilot AI review requested due to automatic review settings July 22, 2026 12:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

eng/pipelines/onebranch/steps/roslyn-analyzers-buildproj-step.yml:179

  • The PR description says the SDL RoslynAnalyzers task was switched to auto mode (userProvideBuildInfo: autoMsBuildInfo) where the task re-runs the detected build. The current template instead performs an explicit DotNetCoreCLI build and uses RoslynAnalyzers in copyLogsOnly: true mode. Please update the PR description (or the implementation) so they match, to avoid future maintainers debugging the wrong behavior.
  - task: securedevelopmentteam.vss-secure-development-tools.build-task-roslynanalyzers.RoslynAnalyzers@3
    displayName: 'Roslyn Analyzers (collect) - build.proj Build${{ parameters.packageShortName }}'
    inputs:
      copyLogsOnly: true
      # Root to search for the *.csproj.*.sarif logs. The analysis build wrote them next to each

Comment thread src/Directory.Build.props
Copilot AI review requested due to automatic review settings July 22, 2026 13:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

eng/pipelines/onebranch/steps/roslyn-analyzers-buildproj-step.yml:203

  • The PR description says the RoslynAnalyzers task was switched to auto mode (autoMsBuildInfo) where it re-runs the detected build command. This template now runs RoslynAnalyzers@3 in copyLogsOnly: true mode and performs the build itself via DotNetCoreCLI, so the implementation and PR description are out of sync. Please update the PR description to reflect the current approach (Copy Logs Only + integrated analyzers), or adjust the YAML to match the described auto-mode behavior.
  # Step 2: Collect the analysis results. In Copy Logs Only mode the task does not build or re-run
  # the compiler -- it just gathers and sanitizes the *.csproj.*.sarif logs produced by Step 1 and
  # hands them to Guardian/SDL. No msBuildVersion / msBuildArchitecture / VS setup is involved, and
  # no OAuth token is needed (nothing is queried from Azure DevOps).
  - task: securedevelopmentteam.vss-secure-development-tools.build-task-roslynanalyzers.RoslynAnalyzers@3
    displayName: 'Roslyn Analyzers (collect) - build.proj Build${{ parameters.packageShortName }}'
    inputs:
      copyLogsOnly: true
      # Root to search for the *.csproj.*.sarif logs. The analysis build wrote them next to each
      # project under the repo checkout; the collector globs this directory recursively.
      logRootDirectory: '$(REPO_ROOT)'

Copilot AI review requested due to automatic review settings July 22, 2026 13:26
@paulmedynski
paulmedynski force-pushed the dev/paul/onebranch-2025-roslyn branch from 46b5a04 to b6762e4 Compare July 22, 2026 13:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

eng/pipelines/onebranch/steps/roslyn-analyzers-buildproj-step.yml:10

  • The PR description says the RoslynAnalyzers step was switched to auto mode (autoMsBuildInfo) where the task re-runs the detected build command. The updated template here instead implements the "integrated analyzers + Copy Logs Only" flow (self-run DotNetCoreCLI build + copyLogsOnly collector) and explicitly states the task performs no build/re-run. Please update the PR description so it matches the actual implementation in this file.
# This template runs Roslyn Analyzers (SDL) against a build.proj target using the RoslynAnalyzers@3
# task from the Secure Development Team's SDL extension, in "Copy Logs Only" mode:
#
# https://eng.ms/docs/cloud-ai-platform/devdiv/one-engineering-system-1es/1es-mohanb/security-integration/guardian-wiki/sdl-azdo-extension/roslyn-analyzers-build-task

@cheenamalhotra cheenamalhotra left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking into results, most of scans are code related recommendations, I would say we should keep this one strictly for official build quality - and locally devs can enable as needed.

Comment thread src/Directory.Build.props Outdated
<!--
Turn on every rule the SDK ships, at the latest level, plus the IDE code-style analyzers.
-->
<AnalysisLevel>latest-all</AnalysisLevel>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we limit it to mandatory and critical only? Set to "Minimum"?

@paulmedynski paulmedynski Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth separating two things here, because they're independent knobs: AnalysisLevel chooses which rules are considered, not which findings break the build.

Severity is decided by each rule's author and enforced by Guardian's Post Analysis (Guardian Break) task, whose threshold defaults to Error only. So widening the rule set with latest-all cannot by itself break anything — warning- and info-level rules are collected into the SARIF for TSA/burn-down (AB#46614) and are simply not breaking.

Two further reasons to keep latest-all:

  1. The whole PropertyGroup is gated on EnableAnalyzers, which defaults to false (build.proj). Developer and CI builds are entirely unaffected — I verified AnalysisLevel evaluates to 8.0 in a normal build and latest-all only under -p:EnableAnalyzers=true, i.e. the dedicated SDL analysis pass, where the breadth is the point.
  2. latest-all was chosen deliberately as a superset of the Guardian task's Sdl.Recommended.Warning.ruleset (see the comparison table in eng/pipelines/onebranch/steps/roslyn-analyzers-buildproj-step.yml). Dropping to minimum would land below the SDL recommended set and risks silently losing required CA3xxx/CA5xxx security rules — a compliance regression rather than just less noise.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommended ruleset is fine, we can handle compiler warnings with suppression file.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — AnalysisLevel is now latest-recommended (58638f9).

One caveat I've written into the property comment so it isn't lost: the SDK's recommended AnalysisMode is not the same rule set as Guardian's private Sdl.Recommended.Warning.ruleset, and the two aren't guaranteed to overlap completely. latest-all was a strict superset of the SDL set and so couldn't under-report, but it also enabled a large volume of rules with no SDL value, so I've taken the narrower set to keep the findings actionable.

If the first real run shows an SDL-required CA3xxx/CA5xxx rule missing from the collected SARIF, the two documented escape hatches are to raise this back to latest-all, or acquire a copy of Sdl.Recommended.Warning.ruleset and point CodeAnalysisRuleSet at it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addendum: the baseline has now been regenerated against latest-recommended (26240.2) and dropped from 835 to 56 entries, leaving 44 real findings across CA1305/CA1309/CA1304/CA2219. Worth noting the baseline is now coupled to this setting — raising it back to latest-all would need a recapture.

Comment thread src/Directory.Build.props Outdated
Comment thread src/Directory.Build.props
overrides the repo-wide TreatWarningsAsErrors=true set above. Genuine compiler errors still
fail the build.
-->
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Always should be True!

@paulmedynski paulmedynski Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The repo-wide default is true, set at the top of this same file, and it stays in force for every normal build. This false lives inside the EnableAnalyzers block, which is off by default, so it applies only to the dedicated SDL analysis pass. Verified by property evaluation: TreatWarningsAsErrors is true in a normal build and false only under -p:EnableAnalyzers=true.

Setting it true in the analysis pass would be counterproductive:

  • the compile halts at the first analyzer warning, truncating the SARIF log Guardian collects — fewer findings reported, not more;
  • every finding gets recorded at SARIF level: error, and since Guardian's break threshold is Error-only, the build would then fail on essentially everything;
  • it contradicts the Guardian RoslynAnalyzers@3 task itself, which passes /p:TreatWarningsAsErrors=false for exactly this reason (see the comparison table in eng/pipelines/onebranch/steps/roslyn-analyzers-buildproj-step.yml).

The comment now states all of that explicitly, including the "analysis pass only" scoping, so the next reader doesn't have to reconstruct it. Happy to discuss further if you still disagree.

@cheenamalhotra cheenamalhotra Aug 20, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Compiler warnings can be suppressed with "suppressionFileForCompilerWarnings" option, I don't agree with the 3rd bullet point - we should follow 1es guidelines here.

In summary, I think we should:

  • Run 'Recommended' ruleset
  • Suppress C# compiler warnings via "suppressionFileForCompilerWarnings"
  • Let errors be thrown for genuine analyzer findings / fix them ASAP (I'd like to see what exactly we get here - IMO there shouldn't be much to do if noisy compiler warnings are suppressed.)
  • Continue on Error to allow running build to completion.
  • Report results to TSA with Roslyn findings.

IMO, if we can handle the compiler warnings correctly, we can safely enable this workflow.

@paulmedynski paulmedynski Aug 28, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should keep it as-is. Reason: warning-clean compilation is already enforced — every job runs a full ordinary build with TreatWarningsAsErrors=true after the analysis pass. Promoting analyzer warnings to errors during analysis adds no coverage; it just fails the analysis build and leaves six steps orange. Run 26240.2 is fully green with no non-succeeded records, and its SARIF coverage is strictly better — SqlClient went from 3 to 15 logs, because previously the compile was dying before finishing.

On suppressionFileForCompilerWarnings: I decompiled the Guardian task to check. The filter is Regex("^CS\\d{4}$") — it only ever targets raw C# compiler warnings, never CA*. So it was never a lever for the analyzer noise we were discussing here.

@github-project-automation github-project-automation Bot moved this from In review to Waiting for customer in SqlClient Board Aug 7, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated no new comments.

paulmedynski added a commit that referenced this pull request Aug 20, 2026
Disable EnforceCodeStyleInBuild in the analysis pass. The IDE* code-style
rules carry no SDL value and add substantial volume to the SARIF logs.
They can still be enabled locally for a deliberate code-style pass.

Clarify why TreatWarningsAsErrors must remain false inside the
EnableAnalyzers block. The repo-wide default of true still governs every
normal build; promoting analyzer warnings to errors here would truncate
the SARIF log Guardian collects and record every finding at SARIF level
"error", which Guardian's Error-only break threshold would then fail the
build on. The Guardian RoslynAnalyzers task passes
/p:TreatWarningsAsErrors=false for the same reason.

Document the Guardian break severity mechanism in both OneBranch
pipelines: the analyzer tasks never break the build, Post Analysis does,
the threshold is cumulative (Error < Warning < Note), and it can be set
globally, per tool, or per job. OneBranch accepts only Error, Warning or
Note, so inheriting the default requires omitting the key.

Update the roslyn-analyzers-buildproj-step.yml comparison table, which
still listed EnforceCodeStyleInBuild among the properties we enable.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated no new comments.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 15 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/Directory.Build.props:48

  • The comment above AnalysisLevel says the analysis pass enables the IDE code-style analyzers, but EnforceCodeStyleInBuild is explicitly set to false immediately below to keep IDExxxx rules off. This is internally inconsistent and could mislead future edits (e.g., someone may assume IDExxxx findings are included in the SARIF logs when they are not).
    <!--
      Turn on every rule the SDK ships, at the latest level, plus the IDE code-style analyzers.
    -->
    <AnalysisLevel>latest-all</AnalysisLevel>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 15 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/Directory.Build.props:48

  • The comment above AnalysisLevel says IDE code-style analyzers are enabled, but this PropertyGroup explicitly sets EnforceCodeStyleInBuild=false (which disables IDE code-style analyzers during build). This is internally contradictory and can mislead future changes to the analyzer configuration.
    <!--
      Turn on every rule the SDK ships, at the latest level, plus the IDE code-style analyzers.
    -->
    <AnalysisLevel>latest-all</AnalysisLevel>

benrr101
benrr101 previously approved these changes Aug 21, 2026
…pelines

- Configure APIScan registration (softwareName, softwareVersion, modeType,
  verbosityLevel) inline in both official and non-official globalSdl blocks
  instead of per-job ob_sdl_apiscan_* variables; keep only DLL/PDB folders
  per-job in build-buildproj-job.
- Register the actual SqlClient APIScan software version (6.10).
- Enable break: true for apiscan, armory, binskim, policheck, and roslyn in
  the official pipeline; make the non-official pipeline non-breaking and
  disable its TSA publishing.
- Drop redundant ob_sdl_binskim_break in onebranch-variables and
  publish-symbols-job (BinSkim disabled there).
- Drive every SDL tool's break value from the breakOnSdlError parameter and
  set tsa.enabled to its negation, instead of relying on OneBranch's implicit
  TSA-based break defaults (which have been buggy in the past).
- Fix CodeQL config: nest enabled under compiled (codeql.compiled.enabled).
- Add eslint break preemptively (kept disabled).
- Add psscriptanalyzer for PowerShell script analysis.
- Document the auto-injected SDL tasks that run without explicit config.
Hardcode TSA bug filing enabled in the official pipeline and disabled in
the non-official pipeline, making it independent of breakOnSdlError. The
non-official pipeline is a manual-only developer-helper build, so it should
not file/update TSA bugs; bug filing is owned by the official pipeline.

Also for non-official: remove the daily schedule (manual-only) and change
the breakOnSdlError default to true.

Expand the tsa block docs in both pipelines to explain how TSA flips each
SDL tool's break default (which we override explicitly via breakOnSdlError)
and that it is otherwise independent of breakOnSdlError.
…ranch build

Squashed history of the onebranch-2025-roslyn work (many intermediate
try/rollback commits collapsed into one).

- Upgrade the OneBranch Windows build container to Windows Server 2025 / vse2026.
- Run the SDL Roslyn analyzers as part of the build via the RoslynAnalyzers task
  in "Copy Logs Only" mode. build.proj gains an EnableAnalyzers flag that is
  forwarded into every leaf "dotnet build", and src/Directory.Build.props enables
  the full analyzer set and emits one SARIF v1 log per project for the task to
  collect (SARIF v1 is what the Guardian sanitizer deserializes).
- Additionally run the internal Microsoft.Internal.Analyzers ("IA*") rules, but
  only in the private ADO.Net pipeline: a gated InternalAnalyzers flag plus a
  runtime-generated NuGet.config pointing at the internal feed. Feed URL and
  version come from the internal-analyzers-variables-v1 variable group, and
  packageSourceMapping keeps the confidential package off the public feed.
- Capture the pre-existing analyzer findings in .config/guardian/.gdnbaselines
  (826) and wire globalSdl.baseline.baselineFile in both the official and
  non-official pipelines, so official builds (roslyn.break: true) only fail on
  NEW findings. Baseline burn-down is tracked in ADO.Net work item #46588.
- roslyn-analyzers step: stop logging the generated NuGet.config (it contains the
  confidential internal feed URL) and log only the path; quote the config path for
  space-safety; verify SARIF via -Filter instead of enumerating every *.sarif;
  clarify packageSourceMapping (longest-prefix match pins Microsoft.Internal.*);
  correct the non-incremental note (no Clean; safety comes from bin redirection).
- build.proj / src/Directory.Build.props: correct the isolated-build note and fix
  the SARIF version in comments (v2 -> v1).
Disable EnforceCodeStyleInBuild in the analysis pass. The IDE* code-style
rules carry no SDL value and add substantial volume to the SARIF logs.
They can still be enabled locally for a deliberate code-style pass.

Clarify why TreatWarningsAsErrors must remain false inside the
EnableAnalyzers block. The repo-wide default of true still governs every
normal build; promoting analyzer warnings to errors here would truncate
the SARIF log Guardian collects and record every finding at SARIF level
"error", which Guardian's Error-only break threshold would then fail the
build on. The Guardian RoslynAnalyzers task passes
/p:TreatWarningsAsErrors=false for the same reason.

Document the Guardian break severity mechanism in both OneBranch
pipelines: the analyzer tasks never break the build, Post Analysis does,
the threshold is cumulative (Error < Warning < Note), and it can be set
globally, per tool, or per job. OneBranch accepts only Error, Warning or
Note, so inheriting the default requires omitting the key.

Update the roslyn-analyzers-buildproj-step.yml comparison table, which
still listed EnforceCodeStyleInBuild among the properties we enable.
CodeQL's "Analyze (csharp)" job has been failing since 1425d0d with
NU1101 for every SqlClient package and NU1100 for
Microsoft.NETFramework.ReferenceAssemblies.net481, both reporting that
PackageSourceMapping excluded the available sources.

That commit removed the packageSourceMapping block from this file on the
assumption it was redundant. It was not. The <clear /> element inside
<packageSources> discards only inherited SOURCES; packageSourceMapping is
merged from ancestor NuGet.config files independently. This PR adds a
mapping to the repo root config that points "*" at the governed source,
and this project clears governed away, so the subtree was left with
nuget.org in no mapping at all and governed mapped but undefined. Every
restore therefore had zero eligible sources.

Restore the mapping and add a <clear /> so the subtree no longer inherits
the root mapping, plus a comment explaining why the section must not be
deleted again.

Local builds masked this because the NuGet global-packages cache is
resolved before any source, so mapping is never exercised on a warm
machine. Verified with a cold cache: restore succeeds with this change
and reproduces the exact CI NU1100/NU1101 errors without it.
Switch the SDL analysis pass to AnalysisLevel=latest-recommended and stop
overriding TreatWarningsAsErrors, so the repo-wide true now applies during
analysis as well.  A warning-clean compile becomes a precondition for
complete Roslyn analysis; the analysis build step gains continueOnError so a
failure still lets the Copy-Logs-Only collector and Guardian Post Analysis
run.

Documents that latest-recommended is the SDK's AnalysisMode and is not
guaranteed to cover Guardian's private Sdl.Recommended.Warning.ruleset, and
records latest-all and acquiring the SDL ruleset as the two escape hatches.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 15 changed files in this pull request and generated 3 comments.

Comment thread src/Directory.Build.props
Comment thread src/Directory.Build.props
Comment thread build.proj
TargetOs no longer has any consumer: "Remove OS-Specific Builds" (#4474)
deleted the conditional compile items and OS constants that read it, so the
-p:TargetOs=Unix passed to BuildSqlClientImpl and the default assignment in
TDS.EndPoint.csproj both set a property nothing reads.

Raised by review feedback on PR #4464.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 16 changed files in this pull request and generated no new comments.

Suppressed comments (3)

Previously missed (3) — in code that hasn't changed since the last review.

NuGet.config:23

  • Add a <clear /> to <packageSourceMapping> here. NuGet merges package source mappings from ancestor/user-level NuGet.config files independently of <packageSources>, so without clearing, a developer’s global mappings can make restores fail (e.g., mapping a package to a source key that isn’t defined in this repo config).
  <packageSourceMapping>
    <!-- Only packages produced by this repository may be restored from the local feed. -->
    <packageSource key="local">

NuGet.analysis.config:18

  • Add a <clear /> to <packageSourceMapping> for the same reason as NuGet.config: mappings are merged from ancestor/user-level configs independently of <packageSources>, and inherited mappings can break restores when this analysis config is selected.
  <packageSourceMapping>
    <!-- Only packages produced by this repository may be restored from the local feed. -->
    <packageSource key="local">

build.proj:186

  • The IsolatedBuildPath comment reads as if all compilation output for the Build* targets is redirected, but BuildSqlClientNotSupported also builds the GenAPI tool (tools/GenAPI) without passing IsolatedBuildPath/ArtifactPath, so that tool still writes to its normal bin/ folder. Clarify the comment scope so future maintainers don’t assume the analysis build is fully isolated for every compiled project in the target graph.
      Description:    When set, redirects each project's binary output (bin) to an isolated
                      location instead of the repo's artifacts/ folder, and forces a full
                      (non-incremental) compile. This lets a build run at any time without
                      clobbering existing real build output - for example, an analysis pass that
                      only needs to compile the code and must not disturb a real build's output.

Restores the analysis-pass override removed in 58638f9.  Every job already
runs a full ordinary build with TreatWarningsAsErrors=true, so promoting
analyzer warnings to errors during analysis adds no coverage -- it only fails
the analysis build and leaves the step showing as a warning in the pipeline.

Drops the continueOnError that existed solely to mask those failures, so a
real analysis-build failure is visible again.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 16 changed files in this pull request and generated 1 comment.

Comment on lines 146 to 152
# Note: BuildNumber and PackageVersion are intentionally omitted. This build only produces
# analyzer logs; versioning has no effect on the analysis.
arguments: >-
-t:Build${{ parameters.packageShortName }}
-p:Configuration=Release
-p:ReferenceType=Package
-p:SkipDependencyPack=true

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, and fixed in 8b544ae — the analysis build now passes -p:BuildNumber and -p:PackageVersion<Suffix>, exactly as the real build step does.

Your mechanism is correct. I verified the version arithmetic with NuGet.Versioning directly:

7.1.0-preview3           satisfies [7.1.0-preview3-dev,8.0.0) = False
7.1.0-preview3.26240.2   satisfies [7.1.0-preview3-dev,8.0.0) = False
7.1.0-preview3-dev       satisfies [7.1.0-preview3-dev,8.0.0) = True

preview3 sorts below preview3-dev (shorter alphanumeric identifier), so the -dev floor is actually higher than every real version and matches nothing — the failure mode would be NU1102 rather than NU1100/NU1101, but the consequence is the same.

One correction to the diagnosis though: this was not currently failing, and it's worth recording why. The OneBranch build jobs export the computed versions as job variables, which surface in the container as environment variables:

SQLCLIENTPACKAGEVERSION=7.1.0-preview3.26240.2
SQLSERVERPACKAGEVERSION=1.1.0-preview1.26240.2

MSBuild reads environment variables as properties, so $(SqlClientPackageVersion) was already set before Versions.props evaluated and the -dev branch was never taken. Confirmed in run 26240.2: all six packages restored with zero NU diagnostics, and reproduced locally — setting only that environment variable yields Microsoft.Data.SqlClient.Internal.Logging = [7.1.0-preview3.26240.2, 8.0.0), an exact match for the packed nupkg.

So the bug was latent rather than active: correct versions were arriving by accident of the environment rather than by intent, and would have broken silently if those variables were renamed or this template reused in a job without them. The fix makes it explicit. I've also removed the "versioning has no effect on the analysis" comment, which was simply wrong.

Rebuilds .config/guardian/.gdnbaselines from build 170697, the first run with
complete Roslyn coverage (TreatWarningsAsErrors=false, so every package
compiles to completion).

56 entries, down from 835:
  * 48 generated by the run (47 from the six package SDL artifacts, 1 from the
    SDL sources stage)
  * 9 non-Roslyn entries carried over from main, 1 of which overlaps

The 781 dropped entries are no longer produced under the current analyzer
configuration: the previous baseline was captured with AnalysisLevel=latest-all
and EnforceCodeStyleInBuild=true (587 CA + 234 IDE + 5 SYSLIB), whereas the
analysis pass now runs latest-recommended with IDE code-style rules off.
Verified reproducible across builds 170470 and 170697.

Adds two previously unbaselined CA1305 findings in Microsoft.Data.SqlClient
(TdsParserStateObjectNative.AssignPendingDNSInfo and
SqlConnectionInternal.TryGetFedAuthTokenLocked).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

eng/pipelines/onebranch/steps/roslyn-analyzers-buildproj-step.yml:152

  • The analysis build runs in ReferenceType=Package but omits BuildNumber / PackageVersion* inputs. In Package mode, sibling package versions are resolved from $(SqlClientPackageVersion) (via Directory.Packages.props), and when no version is provided Versions.props falls back to $(SqlClientNextVersion)-dev (because BuildNumber defaults to 0). That “-dev” floor can be higher than the OneBranch-computed prerelease versions (e.g. 7.1.0-preview3.<Build.BuildNumber>), which can make restores miss the downloaded packages/ artifacts and fail with NU1100/NU1101 or restore the wrong versions from the governed feed.

The analysis build should take the same version inputs as the real build step (at least -p:PackageVersionSqlClient=... / -p:PackageVersionSqlServer=..., and ideally the matching -p:BuildNumber=...), so restores resolve against the exact artifacts for this run.

      # Note: BuildNumber and PackageVersion are intentionally omitted. This build only produces
      # analyzer logs; versioning has no effect on the analysis.
      arguments: >-
        -t:Build${{ parameters.packageShortName }}
        -p:Configuration=Release
        -p:ReferenceType=Package
        -p:SkipDependencyPack=true

Comment on lines +152 to 158
# Run Roslyn analysis. This step is self-contained: it performs its own build into an
# isolated output location, so it can run at any point in the job without clobbering the
# real build output below.
- template: /eng/pipelines/onebranch/steps/roslyn-analyzers-buildproj-step.yml@self
parameters:
dependencyArguments: $(sqlServerVersionArgument)
packageShortName: ${{ parameters.packageShortName }}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 8b544ae — the job now passes revision, versionPropertySuffix and packageVersion through to roslyn-analyzers-buildproj-step.yml, and that template accepts and forwards them as -p:BuildNumber / -p:PackageVersion<Suffix>.

This is the same finding as r3880748092, where I've written up the full verification. Short version: the mechanism you describe is real — I confirmed with NuGet.Versioning that the -dev floor sorts above every real version and matches nothing — but it wasn't actually firing, because the OneBranch jobs export SQLCLIENTPACKAGEVERSION / SQLSERVERPACKAGEVERSION as environment variables and MSBuild picks those up as properties. So the analysis build was getting the right versions ambiently rather than explicitly. The change makes it explicit so it can't drift.

The analysis build runs with ReferenceType=Package, where
SqlClientPackageVersion is the floor of the version range that
Directory.Packages.props pins sibling packages to.  Without BuildNumber and
PackageVersion<Suffix>, Versions.props falls back to
"$(SqlClientNextVersion)-dev", a floor that sorts above every real version and
so matches no package at all.

This has not been failing because the OneBranch jobs export
SqlClientPackageVersion and SqlServerPackageVersion as environment variables,
which MSBuild picks up as properties.  That is ambient state the template
should not depend on, and it would break silently if those variables were
renamed or the template reused elsewhere.

Restores the revision / versionPropertySuffix / packageVersion parameters and
passes them through, so the analysis build resolves the same versions as the
real build by construction.

Raised by review feedback on PR #4464.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

NuGet.config:23

  • packageSourceMapping entries are merged from machine/user NuGet configs independently of <packageSources>. Since this file clears inherited sources, inheriting a more-specific mapping from a higher-level config can leave packages with zero eligible sources (restore failures like NU1100/NU1101). Add <clear /> inside <packageSourceMapping> to make restores deterministic for this repo.
  <packageSourceMapping>
    <!-- Only packages produced by this repository may be restored from the local feed. -->

NuGet.analysis.config:18

  • This config clears inherited sources, but it does not clear inherited packageSourceMapping. Because mappings merge independently, a machine/user NuGet.config with a more-specific mapping can cause analysis restores to have no eligible sources. Add <clear /> under <packageSourceMapping> so the analysis build uses only the mappings defined here.
  <packageSourceMapping>
    <!-- Only packages produced by this repository may be restored from the local feed. -->

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area\Engineering Use this for issues that are targeted for changes in the 'eng' folder or build systems.

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

4 participants