Skip to content

Commit ded207d

Browse files
author
Jesus Zarate
committed
Add catalog signing for non-PE XML content files
Non-PE files (XML docs, templates) cannot carry Authenticode signatures. This adds catalog signing infrastructure: 1. eng/Signing.props: Add FileExtensionSignInfo for .cat so Arcade signs the generated catalog with Microsoft400 2. eng/generate-catalog.ps1: Script to generate CDF and run makecat.exe 3. VisualFSharpDebug.csproj: GenerateCatalogFiles target that runs after VSIX assembly to produce a .cat covering xmlfile.xml Fixes VS signing scan violations for xmlfile.xml (VisualFSharpDebug VSIX). The fsharp.core_13.xml fix for the SDK Swix package is tracked separately (requires VS-repo catalog signing). Bug: https://devdiv.visualstudio.com/DevDiv/_workitems/edit/3008042
1 parent 5d50ace commit ded207d

3 files changed

Lines changed: 183 additions & 0 deletions

File tree

eng/Signing.props

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@
55
<FileSignInfo Include="Newtonsoft.Json.dll" CertificateName="None" />
66
</ItemGroup>
77

8+
<ItemGroup>
9+
<!-- Catalog-sign non-PE content files (XML docs, templates). The .cat file
10+
generated by the GenerateCatalogFiles target covers these files; Arcade
11+
signs the .cat itself with Microsoft400. -->
12+
<FileExtensionSignInfo Include=".cat" CertificateName="Microsoft400" />
13+
</ItemGroup>
14+
815
<PropertyGroup>
916
<UseDotNetCertificate>true</UseDotNetCertificate>
1017
</PropertyGroup>

eng/generate-catalog.ps1

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
<#
2+
.SYNOPSIS
3+
Generates a Catalog Definition File (.cdf) and optionally runs makecat.exe to
4+
produce a signed catalog (.cat) file.
5+
.DESCRIPTION
6+
Recursively scans a directory for files matching a filter and produces a .cdf
7+
file suitable for makecat.exe. Optionally invokes makecat.exe to generate the
8+
.cat file directly.
9+
10+
Used in component team pipelines (Arcade SDK, MicroBuild, or custom) to
11+
catalog-sign customer-modifiable or non-PE files that cannot use direct
12+
Authenticode signing.
13+
14+
If -RunMakecat is specified, the script finds and runs makecat.exe automatically.
15+
Otherwise, it prints the commands to run manually.
16+
.PARAMETER RootPath
17+
The directory containing files to include in the catalog.
18+
.PARAMETER CdfPath
19+
The output path for the .cdf file. If not specified and CatOutputPath is set,
20+
defaults to the CatOutputPath with a .cdf extension.
21+
.PARAMETER CatOutputPath
22+
The path where makecat.exe will create the .cat file. Defaults to the CDF
23+
path with a .cat extension.
24+
.PARAMETER Filter
25+
File filter pattern (e.g., '*.js', '*.xml', '*.ttf'). Default: '*.*' (all files).
26+
.PARAMETER RunMakecat
27+
If specified, finds and runs makecat.exe to produce the .cat file.
28+
.PARAMETER WindowsSdkDir
29+
Optional path to the Windows SDK. Used to locate makecat.exe when -RunMakecat is set.
30+
.PARAMETER ErrorIfMakecatNotFound
31+
If specified with -RunMakecat, throws an error when makecat.exe is not found
32+
instead of warning and skipping. Use in CI/official builds.
33+
.EXAMPLE
34+
# Generate CDF only (print manual steps):
35+
.\New-CatalogDefinitionFile.ps1 -RootPath ".\content" -CdfPath ".\obj\my-files.cdf"
36+
.EXAMPLE
37+
# Generate CDF and run makecat.exe:
38+
.\New-CatalogDefinitionFile.ps1 -RootPath ".\content" -CatOutputPath ".\obj\my-files.cat" -Filter "*.js" -RunMakecat
39+
.EXAMPLE
40+
# CI usage (error if makecat.exe not found):
41+
.\New-CatalogDefinitionFile.ps1 -RootPath "$(_ContentRoot)" -CatOutputPath "$(_CatOutputPath)" -Filter "*.js" -RunMakecat -ErrorIfMakecatNotFound
42+
#>
43+
[CmdletBinding()]
44+
param(
45+
[Parameter(Mandatory)]
46+
[string]$RootPath,
47+
48+
[string]$CdfPath,
49+
50+
[string]$CatOutputPath,
51+
52+
[string]$Filter = '*.*',
53+
54+
[string]$WindowsSdkDir = '',
55+
56+
[switch]$RunMakecat,
57+
58+
[switch]$ErrorIfMakecatNotFound
59+
)
60+
61+
$ErrorActionPreference = 'Stop'
62+
63+
if (-not (Test-Path $RootPath)) {
64+
Write-Error "Root path not found: $RootPath"
65+
return
66+
}
67+
68+
# Resolve paths: need at least one of CdfPath or CatOutputPath
69+
if (-not $CdfPath -and -not $CatOutputPath) {
70+
Write-Error "Specify at least one of -CdfPath or -CatOutputPath."
71+
return
72+
}
73+
if (-not $CatOutputPath) {
74+
$CatOutputPath = [System.IO.Path]::ChangeExtension($CdfPath, '.cat')
75+
}
76+
if (-not $CdfPath) {
77+
$CdfPath = [System.IO.Path]::ChangeExtension($CatOutputPath, '.cdf')
78+
}
79+
80+
# Ensure output directories exist
81+
$cdfDir = Split-Path $CdfPath -Parent
82+
if ($cdfDir -and -not (Test-Path $cdfDir)) {
83+
New-Item -ItemType Directory -Path $cdfDir -Force | Out-Null
84+
}
85+
$catDir = Split-Path $CatOutputPath -Parent
86+
if ($catDir -and -not (Test-Path $catDir)) {
87+
New-Item -ItemType Directory -Path $catDir -Force | Out-Null
88+
}
89+
90+
$files = Get-ChildItem -Path $RootPath -Recurse -Filter $Filter -File
91+
if ($files.Count -eq 0) {
92+
Write-Warning "No files matching '$Filter' found under $RootPath - skipping catalog generation."
93+
return
94+
}
95+
96+
$cdfContent = @()
97+
$cdfContent += "[CatalogHeader]"
98+
$cdfContent += "Name=$CatOutputPath"
99+
$cdfContent += "CatalogVersion=2"
100+
$cdfContent += "HashAlgorithms=SHA256"
101+
$cdfContent += ""
102+
$cdfContent += "[CatalogFiles]"
103+
104+
$i = 0
105+
foreach ($f in $files) {
106+
$ext = $f.Extension.TrimStart('.').ToLower()
107+
$label = "${ext}_${i}_" + ($f.Name -replace '[^\w\.-]', '_')
108+
$cdfContent += "<hash>$label=$($f.FullName)"
109+
$i++
110+
}
111+
112+
$cdfContent | Set-Content -Path $CdfPath -Encoding ASCII
113+
114+
Write-Host "Generated CDF with $($files.Count) file(s) matching '$Filter' at $CdfPath"
115+
116+
if ($RunMakecat) {
117+
# Find makecat.exe — ships with the Windows SDK
118+
$makecat = $null
119+
if ($WindowsSdkDir -and (Test-Path $WindowsSdkDir)) {
120+
$makecat = Get-ChildItem -Path (Join-Path $WindowsSdkDir 'bin') -Recurse -Filter 'makecat.exe' -File |
121+
Where-Object { $_.DirectoryName -match 'x64' } |
122+
Sort-Object DirectoryName -Descending |
123+
Select-Object -First 1
124+
}
125+
if (-not $makecat) { $makecat = Get-Command makecat.exe -ErrorAction SilentlyContinue }
126+
if (-not $makecat) {
127+
$sdkRoot = "${env:ProgramFiles(x86)}\Windows Kits\10\bin"
128+
if (Test-Path $sdkRoot) {
129+
$makecat = Get-ChildItem -Path $sdkRoot -Recurse -Filter 'makecat.exe' -File |
130+
Where-Object { $_.DirectoryName -match 'x64' } |
131+
Sort-Object DirectoryName -Descending |
132+
Select-Object -First 1
133+
}
134+
}
135+
if (-not $makecat) {
136+
if ($ErrorIfMakecatNotFound) {
137+
throw "makecat.exe not found. Catalog signing requires the Windows SDK."
138+
}
139+
Write-Warning "makecat.exe not found - skipping catalog generation. Install Windows SDK for catalog signing."
140+
return
141+
}
142+
143+
$makecatPath = if ($makecat -is [System.Management.Automation.CommandInfo]) { $makecat.Source } else { $makecat.FullName }
144+
Write-Host "Using makecat.exe at: $makecatPath"
145+
146+
& $makecatPath $CdfPath
147+
if ($LASTEXITCODE -ne 0) {
148+
throw "makecat.exe failed with exit code $LASTEXITCODE"
149+
}
150+
151+
Write-Host "Generated catalog file: $CatOutputPath"
152+
} else {
153+
Write-Host ""
154+
Write-Host "Next steps:"
155+
Write-Host " 1. Run: makecat.exe `"$CdfPath`""
156+
Write-Host " 2. Sign: dotnet ddsignfiles.dll -- /file:`"$CatOutputPath`" /certs:Microsoft400"
157+
}

vsintegration/Vsix/VisualFSharpFull/VisualFSharpDebug.csproj

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,23 @@
8484
</Content>
8585
</ItemGroup>
8686
</Target>
87+
88+
<!--
89+
Generate a catalog (.cat) file covering non-PE content files (XML templates)
90+
so the VS signing scan does not flag them as unsigned.
91+
Only runs on Windows (makecat.exe is a Windows SDK tool).
92+
-->
93+
<Target Name="GenerateCatalogFiles" AfterTargets="CreateVsixContainer"
94+
Condition="$([MSBuild]::IsOSPlatform('Windows')) and Exists('$(TargetVsixContainer)')">
95+
<PropertyGroup>
96+
<_ContentRoot>$(TargetVsixContainerIntermediateOutputPath)</_ContentRoot>
97+
<_CatOutputPath>$(TargetVsixContainerIntermediateOutputPath)FSharpDebugContent.cat</_CatOutputPath>
98+
<_ErrorFlag Condition="'$(ContinuousIntegrationBuild)' == 'true' or '$(OfficialBuild)' == 'true'">-ErrorIfMakecatNotFound</_ErrorFlag>
99+
</PropertyGroup>
100+
101+
<Exec Command="powershell.exe -NoProfile -ExecutionPolicy Bypass -Command &quot;&amp; '$(RepoRoot)eng\generate-catalog.ps1' -RootPath '$(_ContentRoot)' -CatOutputPath '$(_CatOutputPath)' -Filter '*.xml' -RunMakecat -WindowsSdkDir '$(WindowsSdkDir)' $(_ErrorFlag)&quot;"
102+
StandardOutputImportance="High"
103+
IgnoreExitCode="true"
104+
Condition="$([MSBuild]::IsOSPlatform('Windows'))" />
105+
</Target>
87106
</Project>

0 commit comments

Comments
 (0)