Skip to content

Commit e62277a

Browse files
committed
feat(ghes): add ghe-base-url input and honor baseUrl for Octokit when uploading SARIF
1 parent 751c204 commit e62277a

File tree

3 files changed

+109
-17
lines changed

3 files changed

+109
-17
lines changed

lib/job-summary.js

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3838
exports.JobSummary = void 0;
3939
const core = __importStar(require("@actions/core"));
4040
const semver_1 = require("semver");
41-
const core_1 = require("@octokit/core");
4241
const github = __importStar(require("@actions/github"));
4342
const util_1 = require("util");
4443
const zlib_1 = require("zlib");
@@ -107,24 +106,38 @@ class JobSummary {
107106
});
108107
}
109108
/**
110-
* Uploads the code scanning SARIF content to the code-scanning GitHub API.
111-
* @param encodedSarif - The final compressed and encoded sarif content.
112-
* @param token - GitHub token to use for the request. Has to have 'security-events: write' permission.
113-
* @private
109+
* Uploads a gzip-compressed, base64-encoded SARIF payload to GitHub Code Scanning.
110+
*
111+
* Uses the current GitHub Actions context (owner, repo, commit SHA, and ref) for the target.
112+
* If a GitHub Enterprise Server base URL is provided via the 'ghe-base-url' or 'ghe_base_url'
113+
* action input, the request is sent to that endpoint; otherwise, it targets github.com.
114+
*
115+
* @param encodedSarif - The SARIF report content after gzip compression and base64 encoding,
116+
* as required by the POST /repos/{owner}/{repo}/code-scanning/sarifs API.
117+
* Typically produced by compressing raw SARIF with gzip and encoding to base64.
118+
* @param token - GitHub token used to authenticate the upload request. Must have
119+
* security_events: write permission on the target repository (e.g., a PAT or GITHUB_TOKEN
120+
* with the appropriate permission).
121+
*
122+
* @returns A promise that resolves when the upload completes successfully.
123+
* @throws Error if the API response status is not 2xx; the thrown error includes the resolved baseUrl
124+
* and a serialized response summary to aid debugging.
114125
*/
115126
static uploadCodeScanningSarif(encodedSarif, token) {
116127
return __awaiter(this, void 0, void 0, function* () {
117-
const octokit = new core_1.Octokit({ auth: token });
118-
let response;
119-
response = yield octokit.request('POST /repos/{owner}/{repo}/code-scanning/sarifs', {
128+
var _a, _b, _c;
129+
const inputBaseUrl = core.getInput('ghe-base-url', { required: false }) || core.getInput('ghe_base_url', { required: false }) || '';
130+
const octokit = inputBaseUrl ? github.getOctokit(token, { baseUrl: inputBaseUrl }) : github.getOctokit(token);
131+
const response = yield octokit.request('POST /repos/{owner}/{repo}/code-scanning/sarifs', {
120132
owner: github.context.repo.owner,
121133
repo: github.context.repo.repo,
122134
commit_sha: github.context.sha,
123135
ref: github.context.ref,
124136
sarif: encodedSarif,
125137
});
126138
if (response.status < 200 || response.status >= 300) {
127-
throw new Error(`Failed to upload SARIF file: ` + JSON.stringify(response));
139+
const usedBaseUrl = ((_c = (_b = (_a = octokit.request) === null || _a === void 0 ? void 0 : _a.endpoint) === null || _b === void 0 ? void 0 : _b.DEFAULTS) === null || _c === void 0 ? void 0 : _c.baseUrl) || 'unknown';
140+
throw new Error(`Failed to upload SARIF file (status ${response.status}). baseUrl=${usedBaseUrl}; response=` + JSON.stringify(response));
128141
}
129142
core.info('SARIF file uploaded successfully');
130143
});

src/job-summary.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,29 @@ export class JobSummary {
105105
}
106106

107107
/**
108-
* Uploads the code scanning SARIF content to the code-scanning GitHub API.
109-
* @param encodedSarif - The final compressed and encoded sarif content.
110-
* @param token - GitHub token to use for the request. Has to have 'security-events: write' permission.
111-
* @private
108+
* Uploads a gzip-compressed, base64-encoded SARIF payload to GitHub Code Scanning.
109+
*
110+
* Uses the current GitHub Actions context (owner, repo, commit SHA, and ref) for the target.
111+
* If a GitHub Enterprise Server base URL is provided via the 'ghe-base-url' or 'ghe_base_url'
112+
* action input, the request is sent to that endpoint; otherwise, it targets github.com.
113+
*
114+
* @param encodedSarif - The SARIF report content after gzip compression and base64 encoding,
115+
* as required by the POST /repos/{owner}/{repo}/code-scanning/sarifs API.
116+
* Typically produced by compressing raw SARIF with gzip and encoding to base64.
117+
* @param token - GitHub token used to authenticate the upload request. Must have
118+
* security_events: write permission on the target repository (e.g., a PAT or GITHUB_TOKEN
119+
* with the appropriate permission).
120+
*
121+
* @returns A promise that resolves when the upload completes successfully.
122+
* @throws Error if the API response status is not 2xx; the thrown error includes the resolved baseUrl
123+
* and a serialized response summary to aid debugging.
112124
*/
113125
private static async uploadCodeScanningSarif(encodedSarif: string, token: string) {
114-
const octokit: Octokit = new Octokit({ auth: token });
115-
let response: OctokitResponse<any> | undefined;
116-
response = await octokit.request('POST /repos/{owner}/{repo}/code-scanning/sarifs', {
126+
const inputBaseUrl = core.getInput('ghe-base-url', { required: false }) || core.getInput('ghe_base_url', { required: false }) || '';
127+
128+
const octokit = inputBaseUrl ? github.getOctokit(token, { baseUrl: inputBaseUrl }) : github.getOctokit(token);
129+
130+
const response = await octokit.request('POST /repos/{owner}/{repo}/code-scanning/sarifs', {
117131
owner: github.context.repo.owner,
118132
repo: github.context.repo.repo,
119133
commit_sha: github.context.sha,
@@ -122,7 +136,8 @@ export class JobSummary {
122136
});
123137

124138
if (response.status < 200 || response.status >= 300) {
125-
throw new Error(`Failed to upload SARIF file: ` + JSON.stringify(response));
139+
const usedBaseUrl = (octokit as any).request?.endpoint?.DEFAULTS?.baseUrl || 'unknown';
140+
throw new Error(`Failed to upload SARIF file (status ${response.status}). baseUrl=${usedBaseUrl}; response=` + JSON.stringify(response));
126141
}
127142

128143
core.info('SARIF file uploaded successfully');
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**
2+
* test/job-summary.sarif.baseurl.spec.ts
3+
* Checking baseUrl selection when loading SARIF on GHES.
4+
*/
5+
6+
import * as core from '@actions/core';
7+
8+
jest.mock('@actions/github', () => {
9+
const actual = jest.requireActual('@actions/github');
10+
return {
11+
...actual,
12+
getOctokit: jest.fn((token: string, opts?: { baseUrl?: string }) => {
13+
const usedBaseUrl = (opts && opts.baseUrl) || process.env.__AUTO_BASE_URL__ || 'https://api.github.com';
14+
15+
const req = jest.fn(async (_route: string, _params: any) => {
16+
(global as any).__USED_BASE_URL__ = usedBaseUrl;
17+
return { status: 201, data: {} };
18+
}) as unknown as any;
19+
20+
req.endpoint = { DEFAULTS: { baseUrl: usedBaseUrl } };
21+
22+
return { request: req } as any;
23+
}),
24+
context: {
25+
repo: { owner: 'o', repo: 'r' },
26+
sha: 'deadbeefdeadbeefdeadbeefdeadbeefdeadbeef',
27+
ref: 'refs/heads/main',
28+
},
29+
};
30+
});
31+
32+
import { JobSummary } from '../src/job-summary';
33+
34+
describe('uploadCodeScanningSarif baseUrl selection (GHES)', () => {
35+
beforeEach(() => {
36+
jest.resetModules();
37+
jest.clearAllMocks();
38+
39+
jest.spyOn(core, 'getInput').mockImplementation((_name: string) => '');
40+
41+
delete process.env.__AUTO_BASE_URL__;
42+
delete (global as any).__USED_BASE_URL__;
43+
});
44+
45+
it('Should use explicit input ghe-base-url if given', async () => {
46+
(core.getInput as jest.Mock).mockImplementation((name: string) => {
47+
if (name === 'ghe-base-url') return 'https://github.enterprise.local/api/v3';
48+
if (name === 'ghe_base_url') return '';
49+
return '';
50+
});
51+
52+
await (JobSummary as any).uploadCodeScanningSarif('eJx4YWJj', 'ghs_token');
53+
54+
expect((global as any).__USED_BASE_URL__).toBe('https://github.enterprise.local/api/v3');
55+
});
56+
57+
it('Should falls back to auto GHES baseUrl via @actions/github if input is not specified', async () => {
58+
process.env.__AUTO_BASE_URL__ = 'https://ghe.corp.local/api/v3';
59+
60+
await (JobSummary as any).uploadCodeScanningSarif('eJx4YWJj', 'ghs_token');
61+
62+
expect((global as any).__USED_BASE_URL__).toBe('https://ghe.corp.local/api/v3');
63+
});
64+
});

0 commit comments

Comments
 (0)