Add code signature validation when installing or updating Azure Functions Core Tools#5126
Add code signature validation when installing or updating Azure Functions Core Tools#5126MicroFish91 wants to merge 36 commits into
Azure Functions Core Tools#5126Conversation
…ions into mwf/verify-func-cli
There was a problem hiding this comment.
Pull request overview
This PR adds post-install/update validation of Azure Functions Core Tools binaries by verifying platform-specific code signatures (Windows Authenticode, macOS codesign), and introduces tests (including a nightly suite) to ensure signature validation behaves correctly.
Changes:
- Run code signature validation after Core Tools install/update flows (npm + Homebrew), with a user prompt on signature failure.
- Add helpers for locating the actual
funcbinary path (including Windows npm shim fallback) and for checking whether signatures are expected for a given platform/version. - Add unit tests plus a nightly test suite that downloads Core Tools from the official CLI feed to validate signatures and detect tampering.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| test/parseFuncCoreToolsPath.test.ts | Unit tests for resolving the func executable path from which/where output. |
| test/nightly/validateFuncCoreToolsCodeSignature/validateFuncCoreToolsCodeSignature.test.ts | Nightly validation of real Core Tools binaries’ signatures and tamper detection. |
| test/nightly/validateFuncCoreToolsCodeSignature/downloadFuncCoreToolsVersions.ts | Downloads/extracts Core Tools zips from the official feed for nightly tests. |
| test/isCodeSignatureExpected.test.ts | Unit tests for platform/version signing expectations. |
| src/utils/cliFeedUtils.ts | Exposes CLI feed URL/types needed to resolve Core Tools download links. |
| src/funcCoreTools/validateFuncCoreToolsCodeSignature.ts | Implements signature validation logic and path resolution. |
| src/funcCoreTools/updateFuncCoreTools.ts | Runs signature validation after updating Core Tools. |
| src/funcCoreTools/uninstallFuncCoreTools.ts | Switches npm package constant naming used for uninstall. |
| src/funcCoreTools/installFuncCoreTools.ts | Runs signature validation after installing Core Tools; updates npm/brew flows. |
| src/funcCoreTools/getFuncPackageManagers.ts | Updates npm package constant naming used for detection. |
| src/funcCoreTools/getBrewPackageName.ts | Uses a dedicated brew package name constant for brew formula naming. |
| src/funcCoreTools/ensureBrewTapTrusted.ts | Adds a trust prompt + tap/trust commands before brew install/upgrade. |
| src/constants.ts | Splits package name constants by package manager (npm vs brew). |
| .vscode/settings.json | Disables JS debug network view to avoid fetch inspection crashes during large nightly downloads. |
|
|
||
| const versionsToTest: FuncVersion[] = Object.values(FuncVersion).filter(v => isCodeSignatureExpected(v)); | ||
|
|
||
| suite.only('validateFuncCoreToolsCodeSignature', function (this: Mocha.Suite): void { |
There was a problem hiding this comment.
Todo: remove .only before merge
| @@ -1,4 +1,9 @@ | |||
| { | |||
| // Node 24's experimental network inspection (node:internal/inspector/network_http) is buggy and | |||
There was a problem hiding this comment.
This randomly started happening after an update. Disabling this flag fixed it for me, will double check this again before merging
| const funcTap: string = 'azure/functions'; | ||
|
|
||
| /** | ||
| * Prompts the user to trust the "azure/functions" Homebrew tap, then taps and trusts it. |
There was a problem hiding this comment.
Newer Homebrew behavior that requires the user to trust azure/functions if they haven't done so before. It's a prerequisite to being able to install core tools.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
| funcLookupOutput = await cpUtils.executeCommand(ext.outputChannel, workspacePath, 'which', composeArgs(withArg('func'))()); | ||
| break; | ||
| case 'win32': | ||
| funcLookupOutput = await cpUtils.executeCommand(ext.outputChannel, workspacePath, 'where.exe', composeArgs(withArg('func'))()); |
There was a problem hiding this comment.
The processutils package already includes the which package, which is a perfect fit for this. Less parsing to do too. I think you can use it indirectly too; IIRC the package exports a getSafeExecPath or similar.
Though, as I think about it, getSafeExecPath may not do anything on Unix. If so, directly using which is fine (but be sure it is declared as a dependency in package.json).
| // strings have no backslash escaping; a literal ' is represented by doubling it (''). | ||
| const escapedPath = cliPath.replace(/'/g, "''"); | ||
| const psCommand = `$sig = Get-AuthenticodeSignature '${escapedPath}'; if ($sig.Status -ne 'Valid') { exit 1 }; $sig.SignerCertificate.Subject`; | ||
| const signingResult = await cpUtils.tryExecuteCommand(ext.outputChannel, undefined, 'powershell', composeArgs(withArg('-Command', psCommand))()); |
There was a problem hiding this comment.
npm has an audit signatures command, not sure if you can scope it to a specific package, but that may be a better option if we know it's installed from NPM (which, I'm not sure we do know)
|
|
||
| async function validateDarwinCodeSignature(cliPath: string): Promise<boolean> { | ||
| // Verify the signature is valid (i.e. the binary has not been tampered with) | ||
| const codeSignResult = await cpUtils.tryExecuteCommand(ext.outputChannel, undefined, 'codesign', composeArgs(withArg('-v', cliPath))()); |
There was a problem hiding this comment.
CLI path probably needs quoted
Add code signature verification that now runs automatically whenever we install / update func core tools.
Code signature verification references used:
Verified tests on both windows and mac.