Build, Sign, and Verify the MIME Validation Plugin¶
This document covers building, signing, and verifying
FsiAgentGovernance.Plugins.dll — the Dataverse pre-validation plugin
shipped from src/ValidateMimeTypePlugin.cs.
For initial registration in Power Platform see
docs/build-instructions.md (legacy Visual
Studio walkthrough) and the Step 2: Register Dataverse Plugin
section of flow-configuration.md.
What ships with each release¶
Starting with the version that resolves Issue #38, every tagged GitHub Release attaches:
| Filename | Purpose |
|---|---|
FsiAgentGovernance.Plugins.dll |
The compiled plugin assembly |
FsiAgentGovernance.Plugins.dll.sha256 |
SHA-256 hash of the DLL |
FsiAgentGovernance.Plugins.dll.sig |
Sigstore signature (legacy split format) |
FsiAgentGovernance.Plugins.dll.pem |
Sigstore signing certificate (legacy split format) |
FsiAgentGovernance.Plugins.dll.bundle |
Sigstore signing bundle (preferred) |
Plus the existing source tarball, SBOMs, root SHA256SUMS, and a build
provenance attestation generated by GitHub Actions.
Strong-name signing. The published DLL is not strong-name signed. Dataverse sandbox isolation requires strong-name signed assemblies, so customers must merge transitive dependencies and apply their own
.snkbefore registration. See Local production build below.
Build locally (CI parity)¶
Prerequisites:
- .NET 8 SDK (build host requires
dotnet; cross-targets net462) - Windows is recommended for full parity with CI; on Linux/macOS, building net462 requires Mono and is not officially supported
cd mime-type-restrictions\src
dotnet restore ValidateMimeTypePlugin.csproj
dotnet build ValidateMimeTypePlugin.csproj `
--configuration Release `
--nologo `
/p:TreatWarningsAsErrors=true
Output: bin\Release\net462\FsiAgentGovernance.Plugins.dll.
Local production build (with strong-name + ILRepack merge)¶
Production builds for Dataverse sandbox isolation require:
- A strong-name key (
.snk). Generate once per environment:
Store this file outside the repo (it is .gitignored). Treat it as a
long-lived signing secret — losing it means existing registered
plugins must be re-deployed under a new public key.
- Build with strong-name signing enabled:
dotnet build ValidateMimeTypePlugin.csproj `
--configuration Release `
--nologo `
/p:SignAssembly=true `
/p:AssemblyOriginatorKeyFile=$env:USERPROFILE\keys\FsiAgentGovernance.Plugins.snk
- Merge transitive dependencies with ILRepack. Sandbox isolation will
not load
System.Text.Jsonor any other NuGet dependency from disk; they must be merged into the plugin DLL.
Install ILRepack (choco install ilrepack or download from
https://github.com/gluck/il-repack/releases) and run:
$bin = "bin\Release\net462"
ilrepack /out:"$bin\FsiAgentGovernance.Plugins.merged.dll" `
"$bin\FsiAgentGovernance.Plugins.dll" `
"$bin\System.Text.Json.dll" `
"$bin\System.Buffers.dll" `
"$bin\System.Memory.dll" `
"$bin\System.Numerics.Vectors.dll" `
"$bin\System.Runtime.CompilerServices.Unsafe.dll" `
"$bin\System.Text.Encodings.Web.dll" `
"$bin\System.Threading.Tasks.Extensions.dll" `
"$bin\System.ValueTuple.dll" `
/keyfile:"$env:USERPROFILE\keys\FsiAgentGovernance.Plugins.snk"
The exact list of transitive DLLs may shift with System.Text.Json
minor releases — include every System.*.dll that ends up in $bin.
- Verify the merged DLL:
Then register FsiAgentGovernance.Plugins.merged.dll via the Plugin
Registration Tool.
CI build (ci-dotnet.yml)¶
.github/workflows/ci-dotnet.yml runs on every push and PR that touches
mime-type-restrictions/src/**. It performs dotnet restore + dotnet
build for both Debug and Release configurations on windows-latest.
The Release artifact is uploaded for inspection and short-retention (14 days). It is not strong-name signed — that is the responsibility of the deployer.
CodeQL (codeql.yml)¶
The CodeQL workflow analyses both Python and C#. The C# analysis runs on
windows-latest, builds the plugin via dotnet build, and uploads
results to the GitHub Security tab.
Release signing (release.yml)¶
When a v*.*.* tag is pushed, the build-plugin job:
- Builds the Release configuration of
ValidateMimeTypePlugin.csproj. - Computes a SHA-256 hash of the output DLL.
- Signs the DLL with cosign keyless OIDC (Sigstore). No long-lived keys are stored in the repository or in repository secrets.
- Generates a build provenance attestation for the DLL.
- Uploads the DLL plus signing artifacts as a workflow artifact.
The downstream release job downloads those artifacts, includes them in
the root SHA256SUMS, and attaches them to the GitHub Release.
Verifying a published DLL¶
Customers who deploy the published binary should verify it before registering it in Dataverse.
1. Verify the SHA-256 hash¶
$expected = (Get-Content FsiAgentGovernance.Plugins.dll.sha256).Split(' ')[0]
$actual = (Get-FileHash -Algorithm SHA256 FsiAgentGovernance.Plugins.dll).Hash.ToLower()
if ($expected -ne $actual) { throw "SHA-256 mismatch" } else { "OK: $actual" }
2. Verify the cosign signature (Sigstore bundle, preferred)¶
cosign verify-blob \
--bundle FsiAgentGovernance.Plugins.dll.bundle \
--certificate-identity-regexp '^https://github.com/judeper/FSI-AgentGov-Solutions/\.github/workflows/release\.yml@refs/tags/v.*$' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
FsiAgentGovernance.Plugins.dll
3. Verify the cosign signature (legacy split format)¶
cosign verify-blob \
--signature FsiAgentGovernance.Plugins.dll.sig \
--certificate FsiAgentGovernance.Plugins.dll.pem \
--certificate-identity-regexp '^https://github.com/judeper/FSI-AgentGov-Solutions/\.github/workflows/release\.yml@refs/tags/v.*$' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
FsiAgentGovernance.Plugins.dll
4. Verify the GitHub build provenance attestation¶
If any of those checks fail, do not register the DLL. File a security
issue per SECURITY.md.
Trust boundary notes¶
- The published DLL is signed by GitHub Actions running on
judeper/FSI-AgentGov-Solutions. Trust depends on the integrity of that repository's release workflow and thevX.Y.Ztag. - Strong-name signing is separate from cosign signing. cosign attests who built the DLL; strong-name attests which assembly identity it loads as in the Dataverse sandbox.
- Customers who require an even stronger trust path may run the Local production build steps above and host their own signed DLL — the cosign-signed reference DLL is then used only as a build artifact for comparison, not as the deployed binary.
See THREAT-MODEL.md "Plugin trust boundary" for
the full threat model.