Skip to content

Commit 7b43b7c

Browse files
committed
Add codeql-action/start-proxy
1 parent 5669f66 commit 7b43b7c

7 files changed

+386
-0
lines changed

lib/start-proxy-action-post.js

+45
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/start-proxy-action-post.js.map

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/start-proxy-action.js

+140
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/start-proxy-action.js.map

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/start-proxy-action-post.ts

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* This file is the entry point for the `post:` hook of `start-proxy-action.yml`.
3+
* It will run after the all steps in this job, in reverse order in relation to
4+
* other `post:` hooks.
5+
*/
6+
import * as core from "@actions/core";
7+
8+
import { wrapError } from "./util";
9+
10+
async function runWrapper() {
11+
try {
12+
const pid = core.getState("proxy-process-pid");
13+
if (pid) {
14+
process.kill(Number(pid));
15+
}
16+
} catch (error) {
17+
core.setFailed(
18+
`start-proxy post-action step failed: ${wrapError(error).message}`,
19+
);
20+
}
21+
}
22+
23+
void runWrapper();

src/start-proxy-action.ts

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import { ChildProcess, spawn } from "child_process";
2+
import * as path from "path";
3+
4+
import * as core from "@actions/core";
5+
import { pki } from "node-forge";
6+
7+
import * as actionsUtil from "./actions-util";
8+
import * as util from "./util";
9+
10+
const PROXY_USER = "proxy_user";
11+
const KEY_SIZE = 2048;
12+
const KEY_EXPIRY_YEARS = 2;
13+
14+
export type CertificateAuthority = {
15+
cert: string;
16+
key: string;
17+
};
18+
19+
export type Credential = {
20+
type: string;
21+
host: string;
22+
username?: string;
23+
password?: string;
24+
token?: string;
25+
};
26+
27+
export type BasicAuthCredentials = {
28+
username: string;
29+
password: string;
30+
};
31+
32+
export type ProxyConfig = {
33+
all_credentials: Credential[];
34+
ca: CertificateAuthority;
35+
proxy_auth?: BasicAuthCredentials;
36+
};
37+
38+
const CERT_SUBJECT = [
39+
{
40+
name: "commonName",
41+
value: "Dependabot Internal CA",
42+
},
43+
{
44+
name: "organizationName",
45+
value: "GitHub ic.",
46+
},
47+
{
48+
shortName: "OU",
49+
value: "Dependabot",
50+
},
51+
{
52+
name: "countryName",
53+
value: "US",
54+
},
55+
{
56+
shortName: "ST",
57+
value: "California",
58+
},
59+
{
60+
name: "localityName",
61+
value: "San Francisco",
62+
},
63+
];
64+
65+
function generateCertificateAuthority(): CertificateAuthority {
66+
const keys = pki.rsa.generateKeyPair(KEY_SIZE);
67+
const cert = pki.createCertificate();
68+
cert.publicKey = keys.publicKey;
69+
cert.serialNumber = "01";
70+
cert.validity.notBefore = new Date();
71+
cert.validity.notAfter = new Date();
72+
cert.validity.notAfter.setFullYear(
73+
cert.validity.notBefore.getFullYear() + KEY_EXPIRY_YEARS,
74+
);
75+
76+
cert.setSubject(CERT_SUBJECT);
77+
cert.setIssuer(CERT_SUBJECT);
78+
cert.setExtensions([{ name: "basicConstraints", cA: true }]);
79+
cert.sign(keys.privateKey);
80+
81+
const pem = pki.certificateToPem(cert);
82+
const key = pki.privateKeyToPem(keys.privateKey);
83+
return { cert: pem, key };
84+
}
85+
86+
async function runWrapper() {
87+
const tempDir = actionsUtil.getTemporaryDirectory();
88+
const logFilePath = path.resolve(tempDir, "proxy.log");
89+
const input = actionsUtil.getOptionalInput("registry_secrets") || "[]";
90+
const credentials = JSON.parse(input) as Credential[];
91+
const ca = generateCertificateAuthority();
92+
const proxy_password = actionsUtil.getOptionalInput("proxy_password");
93+
let proxy_auth: BasicAuthCredentials | undefined = undefined;
94+
if (proxy_password) {
95+
core.setSecret(proxy_password);
96+
proxy_auth = {
97+
username: PROXY_USER,
98+
password: proxy_password,
99+
};
100+
}
101+
const proxyConfig: ProxyConfig = {
102+
all_credentials: credentials,
103+
ca,
104+
proxy_auth,
105+
};
106+
const host = "127.0.0.1";
107+
const proxyBin = path.resolve(__dirname, "..", "bin", "update-job-proxy");
108+
let port = 49152;
109+
try {
110+
let subprocess: ChildProcess | undefined = undefined;
111+
let tries = 5;
112+
let subprocessError: Error | undefined = undefined;
113+
while (tries-- > 0 && !subprocess && !subprocessError) {
114+
subprocess = spawn(
115+
proxyBin,
116+
["-addr", `${host}:${port}`, "-config", "-", "-logfile", logFilePath],
117+
{
118+
detached: true,
119+
stdio: ["pipe", "ignore", "ignore"],
120+
},
121+
);
122+
subprocess.unref();
123+
if (subprocess.pid) {
124+
core.saveState("proxy-process-pid", `${subprocess.pid}`);
125+
}
126+
subprocess.on("error", (error) => {
127+
subprocessError = error;
128+
});
129+
subprocess.on("exit", (code) => {
130+
if (code !== 0) {
131+
port = Math.floor(Math.random() * (65535 - 49152) + 49152);
132+
subprocess = undefined;
133+
}
134+
});
135+
subprocess.stdin?.write(JSON.stringify(proxyConfig));
136+
subprocess.stdin?.end();
137+
// Wait a little to allow the proxy to start
138+
await util.delay(1000);
139+
}
140+
if (subprocessError) {
141+
throw subprocessError;
142+
}
143+
core.info(`Proxy started on ${host}:${port}`);
144+
core.setOutput("proxy_host", host);
145+
core.setOutput("proxy_port", port.toString());
146+
core.setOutput("proxy_ca_certificate", ca.cert);
147+
} catch (error) {
148+
core.setFailed(
149+
`start-proxy action failed: ${util.wrapError(error).message}`,
150+
);
151+
}
152+
}
153+
154+
void runWrapper();

start-proxy/action.yml

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: "CodeQL: Start proxy"
2+
description: "Start HTTP proxy server"
3+
author: "GitHub"
4+
inputs:
5+
registry_secrets:
6+
description: The URLs and credentials of package registries
7+
required: false
8+
default: "[]"
9+
proxy_password:
10+
required: false
11+
description: The password of the proxy
12+
outputs:
13+
proxy_host:
14+
description: The IP address of the proxy
15+
proxy_port:
16+
description: The port of the proxy
17+
proxy_ca_certificate:
18+
description: The proxy's internal CA certificate in PEM format
19+
runs:
20+
using: node20
21+
main: "../lib/start-proxy-action.js"
22+
post: "../lib/start-proxy-action-post.js"

0 commit comments

Comments
 (0)