Install
$ agentstack add skill-dotnet-arcade-skills-binskim-scan ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
BinSkim Local Scanning
Run BinSkim locally against a dotnet repository to find binary security issues. This skill covers installing BinSkim, building the repo, discovering what the official pipeline scans, running BinSkim with matching targeting, and interpreting results.
> Local scans are an approximation. The official pipeline runs BinSkim via Guardian with additional filtering. Local scans produce a superset of official findings. For understanding what the portal reports vs what BinSkim finds, use the binskim-analysis skill. For authoritative pass/fail confirmation, recommend the user manually queue the official CI pipeline against their branch — SDL does not run on PR validation builds.
When to Use This Skill
- Running BinSkim locally to scan binaries for security issues
- Verifying a fix for a BinSkim rule violation before pushing
- Checking whether a repo's scan config covers everything it ships
- Iterating on a fix with fast local feedback
Not for: interpreting official pipeline results (use binskim-analysis), source code security (CodeQL), or credential scanning (CredScan).
Prerequisites
- BinSkim: See [references/binskim-install.md](references/binskim-install.md) for installation.
- Windows:
~\.binskim\extracted\tools\net9.0\win-x64\BinSkim.exe - Linux:
~/.binskim/extracted/tools/net9.0/linux-x64/BinSkim - Build toolchain: .NET SDK (managed builds). For native code: MSVC + CMake on Windows, gcc/clang + CMake on Linux. See [references/build-prereqs.md](references/build-prereqs.md).
- Repo cloned locally: Typically under
C:\git\(Windows) or~/git/(Linux).
Local Scan Workflow
Step 1: Discover Pipeline BinSkim Configuration
Before scanning, read the repo's pipeline YAML to understand what the official scan targets.
- Find the pipeline YAML — look for
azure-pipelines-official.yml,vsts-ci.yml,.vsts-ci.yml,azure-pipelines.yml, or.ado.ymlat the repo root or undereng/pipelines/. SDL is typically only in the CI/official pipeline, not the PR one. - Find the
sdl.binskimsection:
``yaml sdl: binskim: enabled: true scanOutputDirectoryOnly: true ``
- Check for
sdl.binskim.additionalRunConfigParams— custom flags/exclusions. - Identify what artifacts are published — look for
PublishPipelineArtifactsteps. The scan targets these.
> Report what you find. Tell the user: "The official pipeline scans X with config Y. I'll reproduce that locally."
> No SDL config? Some repos have no sdl.binskim section at all. In that case, identify what the pipeline publishes as artifacts (e.g., **\bin\Release\**) and scan that. Note to the user: "This repo has no explicit BinSkim/SDL configuration. Scanning the published artifact directory as a best-effort approximation. Results may differ from any central SDL portal findings."
Step 2: Build the Repo
BinSkim scans built/packaged binaries, not source code. You must build (and often pack) the repo.
# Windows
build.cmd -c Release -pack
# Linux
./build.sh -c Release -pack
What to build depends on pipeline config:
| Pipeline config | Build command | Scan target | |---|---|---| | scanOutputDirectoryOnly + publishes pkgassets | build.cmd -c Release -pack | artifacts\pkgassets\** | | scanOutputDirectoryOnly + publishes NuGet packages | build.cmd -pack then extract .nupkg | Extracted .nupkg contents | | Explicit analyzeTargetGlob | build.cmd -c Release | Use the glob from YAML | | Autobaselining only | build.cmd -c Release | artifacts\bin\** (best guess) |
Extracting .nupkg for scanning (mirrors eng/common/sdl/extract-artifact-packages.ps1):
$nupkgDir = [System.IO.Path]::Combine("artifacts", "packages", "Release", "Shipping")
$extractDir = Join-Path "artifacts" "extracted-for-scan"
Add-Type -AssemblyName System.IO.Compression.FileSystem
Get-ChildItem (Join-Path $nupkgDir "*.nupkg") | ForEach-Object {
$dest = Join-Path $extractDir $_.BaseName
New-Item -ItemType Directory -Path $dest -Force | Out-Null
$zip = [System.IO.Compression.ZipFile]::OpenRead($_.FullName)
$zip.Entries | Where-Object { $_.Name -match '\.(dll|exe|pdb)$' } | ForEach-Object {
$target = Join-Path $dest $_.FullName
New-Item -ItemType Directory -Path (Split-Path $target) -Force | Out-Null
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, $target, $true)
}
$zip.Dispose()
}
> Only extract Shipping packages unless you have reason to scan NonShipping too.
> Native builds may require extra setup. If native code fails to build, you can still scan pre-built native DLLs from the NuGet cache. Many rules (including BA2008) don't need PDBs. See [references/build-prereqs.md](references/build-prereqs.md). > > NuGet cache limitation: This only covers third-party native blobs consumed via NuGet. It misses first-party native code compiled from source, managed assemblies the repo builds, and pack-only artifacts. For repos where all findings are on NuGet-sourced binaries (e.g., machinelearning's Intel DLLs), this is sufficient. For repos that compile native code from source, a full build is needed.
Step 3: Run BinSkim
Preferred: Use the helper script (from the skill's scripts/ directory, or provide its full path):
$script = [System.IO.Path]::Combine("plugins", "dotnet-dnceng", "skills", "binskim-scan", "scripts", "Invoke-BinSkimScan.ps1")
# Scan a directory:
& $script -RepoRoot C:\git\machinelearning -ScanDir (Join-Path "artifacts" "pkgassets")
# Filter to portal-reported rules only:
& $script -RepoRoot C:\git\machinelearning -ScanDir (Join-Path "artifacts" "pkgassets") -PortalRulesFrom C:\temp\Results.sarif
# Auto-discover scan targets:
& $script -RepoRoot C:\git\machinelearning
Manual invocation:
# Adjust path to your platform – see binskim-install.md for installation
$binskim = [System.IO.Path]::Combine($HOME, ".binskim", "tools", "net9.0", "linux-x64", "BinSkim") # Linux
# $binskim = [System.IO.Path]::Combine($HOME, ".binskim", "tools", "net9.0", "win-x64", "BinSkim.exe") # Windows
$targetGlob = Join-Path "artifacts" "pkgassets" "**"
& $binskim analyze $targetGlob --recurse --output binskim-results.sarif --log PrettyPrint --force --Hashes False
> Don't filter to *.dll only — scan ** and let BinSkim decide what's analyzable. This catches .exe, .sys, .so, .dylib, and extensionless Mach-O executables. BinSkim identifies binary format by magic bytes, not file extension.
Step 4: Analyze Results
$sarif = Get-Content -Raw binskim-results.sarif | ConvertFrom-Json
# IMPORTANT: Results.sarif may have multiple runs (BinSkim, roslynanalyzers, etc.)
# Filter to the BinSkim run by tool name — don't assume runs[0]
$binskimRun = $sarif.runs | Where-Object { $_.tool.driver.name -like '*BinSkim*' } | Select-Object -First 1
if (-not $binskimRun) { $binskimRun = $sarif.runs[0] } # fallback
$results = $binskimRun.results
$errors = $results | Where-Object { $_.level -eq 'error' }
$warnings = $results | Where-Object { $_.level -eq 'warning' }
Write-Host "Errors: $($errors.Count), Warnings: $($warnings.Count)"
$errors | Group-Object ruleId | Sort-Object Count -Descending | Format-Table Count, Name
Present results as:
- Summary table: rule ID, count, severity
- Per-binary breakdown for errors
- For each finding: first-party (built in repo) or third-party (from NuGet)?
Step 5: Compare with Official Results (if applicable)
If the user provides official results, map findings and flag:
- Gaps: findings in official but not local (packaging differences)
- Extras: findings local but not official (scanning too broadly, or Guardian filtering)
Local scans are a superset — more findings than the portal is expected. Use -PortalRulesFrom to filter local results to match portal rules.
Cross-Platform
BinSkim ships for Windows, Linux (x64, arm64), and macOS (x64):
- Windows:
tools/net9.0/win-x64/BinSkim.exe - Linux:
tools/net9.0/linux-x64/BinSkim(chmod +x) - macOS:
tools/net9.0/osx-x64/BinSkim(x64 only — no arm64 runtime in package; use Rosetta on Apple Silicon)
Each OS build produces different native binaries with potentially different BinSkim findings. If the repo ships native binaries for multiple platforms, you need to scan binaries from each target OS — not just Windows.
Use build.sh on Linux/macOS to build for those platforms. If the official pipeline only runs BinSkim on Windows legs, flag this as a coverage gap — the pipeline should scan all OS configurations that produce shipped native artifacts.
Cross-platform scanning — any binary on any OS
BinSkim can scan PE, ELF, and Mach-O binaries on any OS. BinSkim identifies binary format by magic bytes (not file extension or host OS). The Windows build can analyze Linux .so files and macOS .dylib files, and vice versa. All the binary parsing is pure managed code with no platform-specific dependencies.
This means you can:
- Scan macOS
.dylibfiles on a Windows dev machine - Scan Linux
.sofiles on a Windows dev machine - Download official build artifacts from any platform and scan them locally, regardless of your OS
The key requirement is passing the right file patterns. BinSkim discovers files via glob patterns you supply (e.g., *.dll). If you only pass *.dll, it won't find .dylib or .so files — not because it can't analyze them, but because the glob doesn't match. Use ** (all files) or explicit patterns like *.dylib *.so to scan non-PE binaries.
> Why do official pipelines miss non-Windows binaries? The 1ES/Guardian SDL template invokes BinSkim with Windows-centric glob patterns (matching .dll, .exe, .sys). Mach-O and ELF binaries are never enumerated — they aren't "rejected", they're simply never passed to BinSkim. This is a configuration gap in how the pipeline invokes BinSkim, not a BinSkim limitation.
Scanning downloaded official artifacts
There are two main scenarios for local BinSkim scanning:
- Verifying a local fix: Build the repo (or sub-repo) locally and scan the output. This is the normal workflow.
- Analyzing platforms not covered by official SDL: If official runs don't scan certain platforms (e.g., Linux/macOS binaries), you can download official build artifacts and scan them locally on any OS — BinSkim can analyze PE, ELF, and Mach-O binaries on any platform. This is also useful for initial triage before fixing anything.
For the artifact download approach, official BinSkim and Guardian SARIF files from the SDL artifacts give you the same information without re-scanning. Re-scanning downloaded artifacts is only needed when official SDL doesn't cover a platform. BinSkim can analyze binaries from any platform on any OS, so you don't need a macOS machine to scan .dylib files.
To scan downloaded artifacts:
- Download build artifacts from AzDO (use
ado-dnceng-pipelines_download_artifactor REST API) - Extract native binaries — artifacts often contain
.tar.gzinside.nupkgor zip files
- For
.tar.gzextraction: usetar.exe(built into Windows 10+) or 7-Zip - PowerShell 7.4+ on .NET 8+ also supports tar via
[System.Formats.Tar.TarFile]::ExtractToDirectory()
- Identify native binaries by file header magic bytes (ELF:
\x7fELF, Mach-O:\xfe\xed\xfa\xce/\xcf\xfa\xed\xfe) - Scan with BinSkim using the appropriate platform binary
Before/After Comparison
To prove a fix works, scan before and after:
# Baseline on main, then fix branch
$before = (Get-Content -Raw binskim-before.sarif | ConvertFrom-Json).runs[0].results | Where-Object { $_.level -eq 'error' }
$after = (Get-Content -Raw binskim-after.sarif | ConvertFrom-Json).runs[0].results | Where-Object { $_.level -eq 'error' }
Write-Host "Before: $($before.Count) errors, After: $($after.Count) errors"
> This requires two full builds. Only use when the user needs proof a fix works.
Fix Strategies
Classify the finding first
- Search the repo for the binary name — is there a project that produces it?
- Check NuGet packages — is it from a
PackageReference? - Check for `` — is it a pre-built file being copied?
Fix by origin
| Binary origin | Example | Fix approach | |---|---|---| | C++ source (.vcxproj) | EtwClrProfiler.dll | Add compiler/linker flags (e.g., /guard:cf) | | Pre-built native from NuGet | Intel MKL/TBB, WiX winterop.dll | Cannot fix here — update package, file upstream, or suppress | | Test framework | xunit.*.dll | Fix scan scope — exclude from shipped artifacts | | Managed C# assembly | Most .dll from .csproj | BA2008 not applicable (BinSkim skips IL-only) |
> Guard in a .csproj does nothing. This MSBuild property only works in .vcxproj (MSVC C++).
VMR fix ownership
In the VMR (dotnet/dotnet), look at the SARIF artifact path to find the source sub-repo (e.g., src/arcade/artifacts/... means the fix goes to dotnet/arcade).
Anti-Patterns
> Don't scan artifacts\bin\ if the pipeline uses scanOutputDirectoryOnly — you'll get findings from test dependencies that aren't shipped.
> Don't skip the pack step — many shippable binaries only materialize during packing.
> Don't assume the native build is required — scan NuGet-cached DLLs if native build fails locally.
> Don't report NuGet transitive dependency findings as repo issues — if libSkiaSharp.dll fails BA2008, that's SkiaSharp's issue.
> Don't be alarmed by extra local findings — use -PortalRulesFrom to filter to portal-reported rules. For rules reference and Guardian filtering details, see the binskim-analysis skill.
References
- Installing BinSkim: [references/binskim-install.md](references/binskim-install.md)
- Build prerequisites: [references/build-prereqs.md](references/build-prereqs.md)
- Per-repo pipeline configs: Use the
binskim-analysisskill for known pipeline names, SDL artifact patterns, and local repro notes per repo - Arcade SDL infrastructure: Use the
binskim-analysisskill forconfigure-sdl-tool.ps1andextract-artifact-packages.ps1details - Rules reference and Guardian filtering: See the binskim-analysis skill
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: dotnet
- Source: dotnet/arcade-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.