Skip to content

Commit

Permalink
Verify DMD downloads with GPG (#12)
Browse files Browse the repository at this point in the history
Fixes #5

DMD does not use HTTPS for download links, thus using GPG signature is
necessary to avoid MitM. LDC currently does not need/provide those.
  • Loading branch information
mihails-strasuns authored Nov 4, 2019
1 parent afc4e8a commit b2dbb38
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 8 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ jobs:
if: (! startsWith(github.ref, 'v'))
run: npm install --production

- run: brew install gnupg
if: matrix.os == 'macOS-latest'

- name: Install D compiler
uses: ./
with:
Expand Down
9 changes: 6 additions & 3 deletions lib/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ function dmd(version) {
: minor !== undefined && minor < 69 ? `${base_url}.windows.zip`
: `${base_url}.windows.7z`,
binpath: "\\dmd2\\windows\\bin",
download_dub: download_dub
download_dub: download_dub,
sig: `${base_url}.windows.7z.sig`
};
case "linux": return {
name: "dmd",
Expand All @@ -90,7 +91,8 @@ function dmd(version) {
: minor !== undefined && minor < 69 ? `${base_url}.linux.zip`
: `${base_url}.linux.tar.xz`,
binpath: "/dmd2/linux/bin64",
download_dub: download_dub
download_dub: download_dub,
sig: `${base_url}.linux.tar.xz.sig`
};
case "darwin": return {
name: "dmd",
Expand All @@ -99,7 +101,8 @@ function dmd(version) {
: minor !== undefined && minor < 69 ? `${base_url}.osx.zip`
: `${base_url}.osx.tar.xz`,
binpath: "/dmd2/osx/bin",
download_dub: download_dub
download_dub: download_dub,
sig: `${base_url}.osx.tar.xz.sig`
};
default:
throw new Error("unsupported platform: " + process.platform);
Expand Down
44 changes: 44 additions & 0 deletions lib/gpg.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const tc = __importStar(require("@actions/tool-cache"));
const promisify_child_process_1 = require("promisify-child-process");
// hack to workaround gpg on windows interaction with paths
function win_path_to_msys(path) {
if (process.platform != "win32")
return path;
path = path.replace('\\', '/');
const drive = path[0];
path = '/' + drive + path.slice(2);
return path;
}
function verify(file_path, sig_url) {
return __awaiter(this, void 0, void 0, function* () {
let keyring = yield tc.downloadTool("https://dlang.org/d-keyring.gpg");
keyring = win_path_to_msys(keyring);
let sig_path = yield tc.downloadTool(sig_url);
sig_path = win_path_to_msys(sig_path);
const gpg_process = promisify_child_process_1.spawn('gpg', ['--lock-never', '--verify', '--keyring', keyring, '--no-default-keyring',
sig_path, file_path], {});
gpg_process.stderr.pipe(process.stdout);
gpg_process.stdout.pipe(process.stdout);
// will throw for non-0 exit status
yield gpg_process;
});
}
exports.verify = verify;
6 changes: 6 additions & 0 deletions lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(require("@actions/core"));
const tc = __importStar(require("@actions/tool-cache"));
const gpg = __importStar(require("./gpg"));
const compiler_1 = require("./compiler");
function run() {
return __awaiter(this, void 0, void 0, function* () {
Expand All @@ -35,6 +36,10 @@ function run() {
else {
console.log(`Downloading ${descr.url}`);
const archive = yield tc.downloadTool(descr.url);
if (descr.sig) {
console.log("Verifying the download with GPG");
yield gpg.verify(archive, descr.sig);
}
const dc_path = yield extract(descr.url, archive);
if (descr.download_dub) {
const dub = yield compiler_1.legacyDub();
Expand All @@ -48,6 +53,7 @@ function run() {
console.log("Done");
}
catch (error) {
console.log(error);
core.setFailed(error.message);
}
});
Expand Down
21 changes: 21 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
"license": "MIT",
"dependencies": {
"@actions/core": "^1.2.0",
"@actions/io": "^1.0.1",
"@actions/tool-cache": "^1.1.2",
"@actions/io": "^1.0.1"
"promisify-child-process": "^3.1.1"
},
"devDependencies": {
"@types/node": "^12.7.4",
Expand Down
10 changes: 7 additions & 3 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface CompilerDescription {
version: string;
url: string;
binpath: string;
sig?: string;
download_dub?: boolean;
}

Expand Down Expand Up @@ -86,7 +87,8 @@ async function dmd(version: string): Promise<CompilerDescription> {
: minor !== undefined && minor < 69 ? `${base_url}.windows.zip`
: `${base_url}.windows.7z`,
binpath: "\\dmd2\\windows\\bin",
download_dub: download_dub
download_dub: download_dub,
sig: `${base_url}.windows.7z.sig`
};
case "linux": return {
name: "dmd",
Expand All @@ -95,7 +97,8 @@ async function dmd(version: string): Promise<CompilerDescription> {
: minor !== undefined && minor < 69 ? `${base_url}.linux.zip`
: `${base_url}.linux.tar.xz`,
binpath: "/dmd2/linux/bin64",
download_dub: download_dub
download_dub: download_dub,
sig: `${base_url}.linux.tar.xz.sig`
};
case "darwin": return {
name: "dmd",
Expand All @@ -104,7 +107,8 @@ async function dmd(version: string): Promise<CompilerDescription> {
: minor !== undefined && minor < 69 ? `${base_url}.osx.zip`
: `${base_url}.osx.tar.xz`,
binpath: "/dmd2/osx/bin",
download_dub: download_dub
download_dub: download_dub,
sig: `${base_url}.osx.tar.xz.sig`
};
default:
throw new Error("unsupported platform: " + process.platform);
Expand Down
29 changes: 29 additions & 0 deletions src/gpg.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as tc from '@actions/tool-cache';
import { spawn } from 'promisify-child-process';

// hack to workaround gpg on windows interaction with paths
function win_path_to_msys(path: string) {
if (process.platform != "win32")
return path;
path = path.replace('\\', '/')
const drive = path[0];
path = '/' + drive + path.slice(2)
return path;
}

export async function verify(file_path: string, sig_url: string) {
let keyring = await tc.downloadTool("https://dlang.org/d-keyring.gpg");
keyring = win_path_to_msys(keyring);
let sig_path = await tc.downloadTool(sig_url);
sig_path = win_path_to_msys(sig_path);
const gpg_process = <any> spawn(
'gpg',
[ '--lock-never', '--verify', '--keyring', keyring, '--no-default-keyring',
sig_path, file_path ],
{}
);
gpg_process.stderr.pipe(process.stdout);
gpg_process.stdout.pipe(process.stdout);
// will throw for non-0 exit status
await gpg_process;
}
9 changes: 8 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as core from '@actions/core';
import * as tc from '@actions/tool-cache';
import { mkdirP } from '@actions/io';
import * as gpg from './gpg';

import { compiler, legacyDub } from './compiler';

Expand All @@ -23,7 +24,12 @@ async function run() {
}
else {
console.log(`Downloading ${descr.url}`);
const archive = await tc.downloadTool(descr.url);
const archive = await tc.downloadTool(descr.url);
if (descr.sig)
{
console.log("Verifying the download with GPG");
await gpg.verify(archive, descr.sig);
}
const dc_path = await extract(descr.url, archive);

if (descr.download_dub) {
Expand All @@ -39,6 +45,7 @@ async function run() {
core.exportVariable("DC", descr.name);
console.log("Done");
} catch (error) {
console.log(error);
core.setFailed(error.message);
}
}
Expand Down

0 comments on commit b2dbb38

Please sign in to comment.