Skip to content

Commit f9290a1

Browse files
committed
Add catalog signing for .js files in Mono workload packs
The VS signing scan requires every signable file to carry its own signature. The Mono workload packs contain 198 unsigned .js files (browser-wasm runtime scripts) that are customer-modifiable. For .js files: keep CertificateName=None (same as dotnet/emsdk#1671) because these are customer-modifiable runtime files. Instead, generate a .cat catalog file covering all .js files, signed via FileExtensionSignInfo for .cat. The GenerateCatalogFiles target runs after AddMonoRuntimeFiles on Windows browser-wasm builds. For .cab files: moved to dotnet/arcade (dotnet/arcade#16742) so all Arcade-based repos get correct cab signing by default. Tracking: https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2911494
1 parent 335fa34 commit f9290a1

3 files changed

Lines changed: 123 additions & 2 deletions

File tree

eng/Signing.props

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,12 @@
4949
<MacOSPkg Include="$(ArtifactsPackagesDir)**/dotnet-runtime*.pkg" Exclude="$(ArtifactsPackagesDir)**/dotnet-runtime-internal*.pkg" />
5050
<FileSignInfo Include="@(MacOSPkg->'%(Filename)%(Extension)')" CertificateName="MacDeveloperWithNotarization" />
5151

52-
<!-- We don't need to code sign .js files because they are not used in Windows Script Host. -->
53-
<!-- WARNING: Needs to happed outside of any target -->
52+
<!-- JS files are customer-modifiable runtime/toolchain files. They cannot be
53+
Authenticode-signed because modifying a signed file breaks the signature.
54+
Instead, a catalog file (.cat) is generated and signed to provide integrity
55+
verification. See the GenerateCatalogFiles target in eng/mono.proj. -->
5456
<FileExtensionSignInfo Update=".js" CertificateName="None" />
57+
<FileExtensionSignInfo Include=".cat" CertificateName="Microsoft400" />
5558

5659
<!-- Third-party components which should be signed. -->
5760
<FileSignInfo Include="Antlr4.Runtime.Standard.dll" CertificateName="3PartySHA2" />

eng/generate-catalog.ps1

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<#
2+
.SYNOPSIS
3+
Generates a catalog definition file (.cdf) and catalog file (.cat) for all .js files
4+
in the specified root directory. Used for VS signing compliance - the .js files are
5+
customer-modifiable and cannot be directly Authenticode-signed.
6+
7+
.PARAMETER RootPath
8+
Root directory to search for .js files recursively.
9+
10+
.PARAMETER CatOutputPath
11+
Full path for the output .cat file.
12+
#>
13+
param(
14+
[Parameter(Mandatory)][string]$RootPath,
15+
[Parameter(Mandatory)][string]$CatOutputPath,
16+
[string]$WindowsSdkDir = '',
17+
[switch]$ErrorIfMakecatNotFound
18+
)
19+
20+
$ErrorActionPreference = 'Stop'
21+
22+
$cdfPath = [System.IO.Path]::ChangeExtension($CatOutputPath, '.cdf')
23+
24+
$files = Get-ChildItem -Path $RootPath -Recurse -Filter '*.js' -File
25+
if ($files.Count -eq 0) {
26+
Write-Warning "No .js files found under $RootPath - skipping catalog generation."
27+
exit 0
28+
}
29+
30+
$cdf = @()
31+
$cdf += '[CatalogHeader]'
32+
$cdf += "Name=$CatOutputPath"
33+
$cdf += 'CatalogVersion=2'
34+
$cdf += 'HashAlgorithms=SHA256'
35+
$cdf += ''
36+
$cdf += '[CatalogFiles]'
37+
38+
$i = 0
39+
foreach ($f in $files) {
40+
$label = "js_${i}_" + ($f.Name -replace '[^\w\.-]', '_')
41+
$cdf += "<hash>$label=$($f.FullName)"
42+
$i++
43+
}
44+
45+
$cdf | Set-Content -Path $cdfPath -Encoding ASCII
46+
Write-Host "Generated CDF with $($files.Count) .js files at $cdfPath"
47+
48+
$catDir = [System.IO.Path]::GetDirectoryName($CatOutputPath)
49+
if (-not (Test-Path $catDir)) {
50+
New-Item -ItemType Directory -Path $catDir -Force | Out-Null
51+
}
52+
53+
# Find makecat.exe - it ships with the Windows SDK and may not be on PATH.
54+
$makecat = $null
55+
if ($WindowsSdkDir -and (Test-Path $WindowsSdkDir)) {
56+
$makecat = Get-ChildItem -Path (Join-Path $WindowsSdkDir 'bin') -Recurse -Filter 'makecat.exe' -File |
57+
Where-Object { $_.DirectoryName -match 'x64' } |
58+
Sort-Object DirectoryName -Descending |
59+
Select-Object -First 1
60+
}
61+
62+
if (-not $makecat) {
63+
$makecat = Get-Command makecat.exe -ErrorAction SilentlyContinue
64+
}
65+
66+
if (-not $makecat) {
67+
# Fallback: search common Windows SDK locations
68+
$sdkRoot = "${env:ProgramFiles(x86)}\Windows Kits\10\bin"
69+
if (Test-Path $sdkRoot) {
70+
$makecat = Get-ChildItem -Path $sdkRoot -Recurse -Filter 'makecat.exe' -File |
71+
Where-Object { $_.DirectoryName -match 'x64' } |
72+
Sort-Object DirectoryName -Descending |
73+
Select-Object -First 1
74+
}
75+
}
76+
77+
if (-not $makecat) {
78+
if ($ErrorIfMakecatNotFound) {
79+
throw "makecat.exe not found. Catalog signing requires the Windows SDK which must be available in CI builds."
80+
}
81+
Write-Warning "makecat.exe not found - skipping catalog generation. Catalog signing requires the Windows SDK."
82+
exit 0
83+
}
84+
85+
$makecatPath = if ($makecat -is [System.Management.Automation.CommandInfo]) { $makecat.Source } else { $makecat.FullName }
86+
Write-Host "Using makecat.exe at: $makecatPath"
87+
88+
& $makecatPath $cdfPath
89+
if ($LASTEXITCODE -ne 0) {
90+
throw "makecat.exe failed with exit code $LASTEXITCODE"
91+
}
92+
93+
Write-Host "Generated catalog file: $CatOutputPath"

src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.Mono.sfxproj

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,29 @@
7070
<ReferenceCopyLocalPaths Include="@(MonoRuntimeFiles)" />
7171
</ItemGroup>
7272
</Target>
73+
74+
<!--
75+
Generate a catalog (.cat) file covering all .js files in the runtime pack.
76+
The .js files are customer-modifiable runtime/toolchain files that cannot be
77+
directly Authenticode-signed. The .cat provides integrity verification without
78+
preventing modification. Only runs on Windows (makecat.exe is a Windows SDK tool)
79+
and only for browser-wasm packs which contain .js files.
80+
-->
81+
<Target Name="GenerateCatalogFiles" AfterTargets="AddMonoRuntimeFiles"
82+
Condition="$([MSBuild]::IsOSPlatform('Windows')) and '$(RuntimeIdentifier)' == 'browser-wasm'">
83+
<PropertyGroup>
84+
<_CatOutputPath>$(ArtifactsObjDir)mono-pack\mono-runtime-js.cat</_CatOutputPath>
85+
<_ErrorIfMakecatNotFound Condition="'$(ContinuousIntegrationBuild)' == 'true' or '$(OfficialBuild)' == 'true'">-ErrorIfMakecatNotFound</_ErrorIfMakecatNotFound>
86+
</PropertyGroup>
87+
88+
<Exec Command="powershell.exe -NoProfile -ExecutionPolicy Bypass -Command &quot;&amp; '$(RepoRoot)eng\generate-catalog.ps1' -RootPath '$(MonoObjDir)' -CatOutputPath '$(_CatOutputPath)' -WindowsSdkDir '$(WindowsSdkDir)' $(_ErrorIfMakecatNotFound)&quot;" StandardOutputImportance="High" />
89+
90+
<ItemGroup Condition="Exists('$(_CatOutputPath)')">
91+
<ReferenceCopyLocalPaths Include="$(_CatOutputPath)">
92+
<TargetPath>runtimes/$(RuntimeIdentifier)/native/mono-runtime-js.cat</TargetPath>
93+
<ExcludeFromDataFiles>true</ExcludeFromDataFiles>
94+
</ReferenceCopyLocalPaths>
95+
</ItemGroup>
96+
<Message Condition="Exists('$(_CatOutputPath)')" Importance="High" Text="Generated catalog file: $(_CatOutputPath)" />
97+
</Target>
7398
</Project>

0 commit comments

Comments
 (0)