Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1436e90
Add verify-signature plumbing and Temurin verification support
Copilot Jun 17, 2026
39460f2
Rebuild dist after signature verification changes
Copilot Jun 17, 2026
70770c9
Refine signature verification errors and regenerate dist
Copilot Jun 17, 2026
b2f4bcb
refactor: make gpg.ts generic, move Adoptium-specific constant to tem…
Copilot Jun 17, 2026
fc8acd0
fix: mock renameWinArchive in temurin tests and add signature e2e job
Copilot Jun 17, 2026
c607d20
Merge pull request #5 from johnoliver/copilot/fix-github-actions-job-…
johnoliver Jun 17, 2026
a3589a9
refactor: bundle Adoptium public key, replace keyserver lookup with l…
Copilot Jun 24, 2026
33264d6
feat: add verify-signature-public-key input to allow custom GPG key o…
Copilot Jun 24, 2026
40f7b5c
refactor: extract Adoptium public key to adoptium-key.ts; tighten gpg…
Copilot Jun 24, 2026
3f3ac23
Add verify-signature plumbing and Temurin verification support
johnoliver Jun 24, 2026
c2ac82f
Potential fix for pull request finding
johnoliver Jun 24, 2026
03daa99
Potential fix for pull request finding
johnoliver Jun 24, 2026
db1f1b8
Add Microsoft signature verification support
Copilot Jun 25, 2026
2b15efd
Regenerate dist bundles for Microsoft signature checks
Copilot Jun 25, 2026
5dab176
Harden Microsoft signature URL handling
Copilot Jun 25, 2026
2c76c5e
Merge branch 'copilot/include-signature-verification' into signature-4
johnoliver Jun 25, 2026
776fcf9
Add setup-java-microsoft-signature-verification e2e job
Copilot Jun 25, 2026
2aeafef
Merge remote-tracking branch 'origin/copilot/include-signature-verifi…
johnoliver Jun 25, 2026
45cdfed
chore: regenerate dist files
Copilot Jun 25, 2026
673ccf9
Fix e2e-versions: remove duplicate job, update signature jobs to chec…
Copilot Jun 25, 2026
e2b8899
Fix Prettier formatting in test files
Copilot Jun 25, 2026
730e373
fix: mock renameWinArchive in microsoft-installer tests to fix Window…
Copilot Jun 25, 2026
8012407
fix: use --homedir flag instead of GNUPGHOME env var for Windows GPG …
Copilot Jun 25, 2026
2c98690
fix: convert Windows paths to POSIX format for MSYS2 GPG on Windows
Copilot Jun 25, 2026
165ecdd
Fix gpg test formatting
Copilot Jun 25, 2026
118154c
Merge branch 'main' into signature-4
brunoborges Jun 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add Microsoft signature verification support
  • Loading branch information
Copilot authored Jun 25, 2026
commit db1f1b87544f03c66ad108ff152c3e03c87c9c0a
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ For information about the latest releases, recent updates, and newly supported d

- `check-latest`: Setting this option makes the action to check for the latest available version for the version spec.

- `verify-signature`: Verifies downloaded Java package signatures when supported by the selected distribution. Currently supported for `temurin`. If set to `true` for unsupported distributions, the action fails.
- `verify-signature`: Verifies downloaded Java package signatures when supported by the selected distribution. Currently supported for `temurin` and `microsoft`. If set to `true` for unsupported distributions, the action fails.

- `cache`: Quick [setup caching](#caching-packages-dependencies) for the dependencies managed through one of the predefined package managers. It can be one of "maven", "gradle" or "sbt".

Expand Down
125 changes: 124 additions & 1 deletion __tests__/distributors/microsoft-installer.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import {MicrosoftDistributions} from '../../src/distributions/microsoft/installer';
import {
MicrosoftDistributions,
MICROSOFT_PUBLIC_KEY
} from '../../src/distributions/microsoft/installer';
import os from 'os';
import data from '../data/microsoft.json';
import * as httpm from '@actions/http-client';
import * as core from '@actions/core';
import * as tc from '@actions/tool-cache';
import * as gpg from '../../src/gpg';
import * as util from '../../src/util';
import fs from 'fs';

describe('findPackageForDownload', () => {
let distribution: MicrosoftDistributions;
Expand Down Expand Up @@ -97,6 +104,7 @@ describe('findPackageForDownload', () => {
.replace('{{OS_TYPE}}', os)
.replace('{{ARCHIVE_TYPE}}', archive);
expect(result.url).toBe(url);
expect(result.signatureUrl).toBe(`${url}.sig`);
});

it.each([
Expand Down Expand Up @@ -183,3 +191,118 @@ describe('findPackageForDownload', () => {
);
});
});

describe('downloadTool', () => {
let spyDownloadTool: jest.SpyInstance;
let spyExtractJdkFile: jest.SpyInstance;
let spyCacheDir: jest.SpyInstance;
let spyVerifySignature: jest.SpyInstance;
let distribution: MicrosoftDistributions;

beforeEach(() => {
jest
.spyOn(os, 'platform')
.mockReturnValue(process.platform as ReturnType<typeof os.platform>);

distribution = new MicrosoftDistributions({
version: '17',
architecture: 'x64',
packageType: 'jdk',
checkLatest: false
});

spyDownloadTool = jest.spyOn(tc, 'downloadTool');
spyDownloadTool.mockImplementation(async () => {
return '/tmp/jdk.tar.gz';
});

spyExtractJdkFile = jest.spyOn(util, 'extractJdkFile');
spyExtractJdkFile.mockImplementation(async () => {
return '/tmp/unpacked';
});

jest.spyOn(fs, 'readdirSync').mockReturnValue(['jdk'] as any);
spyCacheDir = jest.spyOn(tc, 'cacheDir');
spyCacheDir.mockImplementation(async () => {
return '/tmp/cached';
});

spyVerifySignature = jest.spyOn(gpg, 'verifyPackageSignature');
spyVerifySignature.mockImplementation(async () => {});
});

afterEach(() => {
jest.restoreAllMocks();
});

it('verifies signature when enabled', async () => {
const signedDistribution = new MicrosoftDistributions({
version: '17',
architecture: 'x64',
packageType: 'jdk',
checkLatest: false,
verifySignature: true
});

await signedDistribution['downloadTool']({
version: '17.0.14+7',
url: 'https://example.com/jdk.tar.gz',
signatureUrl: 'https://example.com/jdk.tar.gz.sig'
});

expect(spyVerifySignature).toHaveBeenCalledWith(
'/tmp/jdk.tar.gz',
'https://example.com/jdk.tar.gz.sig',
MICROSOFT_PUBLIC_KEY
);
});

it('uses custom public key when verifySignaturePublicKey is provided', async () => {
const customKey =
'-----BEGIN PGP PUBLIC KEY BLOCK-----\ncustom\n-----END PGP PUBLIC KEY BLOCK-----';
const signedDistribution = new MicrosoftDistributions({
version: '17',
architecture: 'x64',
packageType: 'jdk',
checkLatest: false,
verifySignature: true,
verifySignaturePublicKey: customKey
});

await signedDistribution['downloadTool']({
version: '17.0.14+7',
url: 'https://example.com/jdk.tar.gz',
signatureUrl: 'https://example.com/jdk.tar.gz.sig'
});

expect(spyVerifySignature).toHaveBeenCalledWith(
'/tmp/jdk.tar.gz',
'https://example.com/jdk.tar.gz.sig',
customKey
);
});

it('fails when signature is missing and verification is enabled', async () => {
const signedDistribution = new MicrosoftDistributions({
version: '17',
architecture: 'x64',
packageType: 'jdk',
checkLatest: false,
verifySignature: true
});

await expect(
signedDistribution['downloadTool']({
version: '17.0.14+7',
url: 'https://example.com/jdk.tar.gz'
})
).rejects.toThrow(
"Input 'verify-signature' is enabled, but no signature URL was found for Microsoft Build of OpenJDK version 17.0.14+7."
);
expect(spyVerifySignature).not.toHaveBeenCalled();
});

it('supports signature verification', () => {
expect(distribution['supportsSignatureVerification']()).toBe(true);
});
});
31 changes: 31 additions & 0 deletions src/distributions/microsoft/installer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ import {
getGitHubHttpHeaders,
renameWinArchive
} from '../../util';
import * as gpg from '../../gpg';
import {MICROSOFT_PUBLIC_KEY} from './microsoft-key';
import * as core from '@actions/core';
import * as tc from '@actions/tool-cache';
import fs from 'fs';
import path from 'path';
import {TypedResponse} from '@actions/http-client/lib/interfaces';

export {MICROSOFT_PUBLIC_KEY} from './microsoft-key';

export class MicrosoftDistributions extends JavaBase {
constructor(installerOptions: JavaInstallerOptions) {
super('Microsoft', installerOptions);
Expand All @@ -29,6 +33,28 @@ export class MicrosoftDistributions extends JavaBase {
);
let javaArchivePath = await tc.downloadTool(javaRelease.url);

if (this.verifySignature) {
if (!javaRelease.signatureUrl) {
throw new Error(
`Input 'verify-signature' is enabled, but no signature URL was found for Microsoft Build of OpenJDK version ${javaRelease.version}.`
);
}
core.info(`Verifying Java package signature...`);
try {
await gpg.verifyPackageSignature(
javaArchivePath,
javaRelease.signatureUrl,
this.verifySignaturePublicKey ?? MICROSOFT_PUBLIC_KEY
);
} catch (error) {
throw new Error(
`Failed to verify signature for Microsoft Build of OpenJDK version ${javaRelease.version} from ${javaRelease.signatureUrl}: ${
(error as Error).message
}`
);
}
}

core.info(`Extracting Java archive...`);
const extension = getDownloadArchiveExtension();
if (process.platform === 'win32') {
Expand Down Expand Up @@ -82,10 +108,15 @@ export class MicrosoftDistributions extends JavaBase {

return {
url: foundRelease.files[0].download_url,
signatureUrl: `${foundRelease.files[0].download_url}.sig`,
version: foundRelease.version
};
}

protected supportsSignatureVerification(): boolean {
return true;
}

private async getAvailableVersions(): Promise<tc.IToolRelease[] | null> {
// TODO get these dynamically!
// We will need Microsoft to add an endpoint where we can query for versions.
Expand Down
21 changes: 21 additions & 0 deletions src/distributions/microsoft/microsoft-key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Microsoft Build of OpenJDK GPG signing key
// Retrieved from: https://download.visualstudio.microsoft.com/download/pr/b90071e2-e0cf-4411-98be-dbeb09d67bf0/8622862bcd54206e158c5abca0582c9b/464279_464280_aoc_20210208.asc
export const MICROSOFT_PUBLIC_KEY = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: BSN Pgp v1.1.0.0

mQENBGAhlWcBCADCQjj6huLTenvZSLej35e9YKEHm4lix2uvPOONexMaU8V2v7KL
RGdoXF7jwHci7efnPZ+9zpS2+g3rhvv8M7yWy9E/1psEtGzvmp1IL/qIabMEQqi+
UlhPGh7MQ/BkXAlic8Dyl3XYqr0EXS11iCiTr6Zkxs9Ee4V54gxL4gogRn4wk9sl
/nrjgDzMsUwla0pynoQQvYpqCdiAr3gKKllT1skCDqgVOMMyZxsx9HjZxg/3AJz6
r5i512L2R+3Hkv+XmxT+mnGBCFcny0DM7PjNXEmIK3ZSkro1tQML90zx3Fyh5esx
fpVvuIXGFV75o35VVCBZoiD3hcfOnIJsPQ9nABEBAAG0OE1pY3Jvc29mdCBKYXZh
IEVuZ2luZWVyaW5nIDxqYXZhcGxhdGluZnJhQG1pY3Jvc29mdC5jb20+iQE4BBMB
CAAiBQJgIZVnAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRA1Ux0xWyHB
icwTCACJO2FGNocNvdUtAb+eDKuGwt0chAJdCES2ZtgBScwrwDyWpxpRznoXWBHL
MJeLyxJoKsCG3vVlY4uh48psCzVm3OKvi7MCPT955t8W6TzfSBxTpjR8zRgJkjPJ
EGhHTlusUfz7TtM5etJF0qscSJH1grcNsgtee97mk4QyEzT8Di83NQmYxKcBrliq
yK/SWWt8VkTyYAEO6L5PoB4L9r8ka27uQs+jgCw+/Z0JMtNmmhyNGY3+a1YtPeoy
JdQaI9LphfKGbVaz6SK2aol7vj+c2TG3TLUYdOYGMH1OZlri2GTkCVjwna2GC7p4
Fa133tP85xzJEq1XeXm8WeLFo2wV
=rHCS
-----END PGP PUBLIC KEY BLOCK-----`;