Add step-security insights url in job summary (#227)

This commit is contained in:
Varun Sharma 2023-01-13 10:30:06 -08:00 committed by GitHub
commit 18bf8ad2ca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 3590 additions and 2171 deletions

36
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,36 @@
name: Test
on:
pull_request:
branches:
- main
push:
branches:
- main # to update code coverage
permissions: # added using https://github.com/step-security/secure-workflows
contents: read
concurrency:
group: ${{ github.workflow }}
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@ebacdc22ef6c2cfb85ee5ded8f2e640f4c776dd5 # v2.0.0
with:
disable-sudo: true
egress-policy: block
allowed-endpoints: >
codecov.io:443
github.com:443
registry.npmjs.org:443
storage.googleapis.com:443
uploader.codecov.io:443
- name: Checkout
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- name: Install Dependencies
run: npm ci
- name: Run coverage
run: npm test -- --coverage
- uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b # v2.1.0

36
dist/index.js vendored
View file

@ -2832,16 +2832,40 @@ var __webpack_exports__ = {};
// ESM COMPAT FLAG
__nccwpck_require__.r(__webpack_exports__);
// EXTERNAL MODULE: ./node_modules/@actions/core/lib/core.js
var lib_core = __nccwpck_require__(186);
;// CONCATENATED MODULE: ./src/common.ts
var __awaiter = (undefined && undefined.__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());
});
};
function printInfo(web_url) {
console.log("\x1b[32m%s\x1b[0m", "View security insights and recommended policy at:");
console.log(`${web_url}/github/${process.env["GITHUB_REPOSITORY"]}/actions/runs/${process.env["GITHUB_RUN_ID"]}`);
}
function addSummary() {
return __awaiter(this, void 0, void 0, function* () {
if (process.env.STATE_monitorStatusCode === "200") {
const web_url = "https://app.stepsecurity.io";
const insights_url = `${web_url}/github/${process.env["GITHUB_REPOSITORY"]}/actions/runs/${process.env["GITHUB_RUN_ID"]}`;
yield core.summary
.addSeparator()
.addImage("https://github.com/step-security/harden-runner/raw/main/images/banner.png", "StepSecurity Harden-Runner", { width: "200" })
.addLink("View security insights and recommended policy", insights_url)
.addSeparator()
.write();
}
});
}
const CONTAINER_MESSAGE = "This job is running in a container. Harden Runner does not run in a container as it needs sudo access to run. This job will not be monitored.";
const UBUNTU_MESSAGE = "This job is not running in a GitHub Actions Hosted Runner Ubuntu VM. Harden Runner is only supported on Ubuntu VM. This job will not be monitored.";
// EXTERNAL MODULE: ./node_modules/@actions/core/lib/core.js
var core = __nccwpck_require__(186);
;// CONCATENATED MODULE: external "node:fs"
const external_node_fs_namespaceObject = require("node:fs");
;// CONCATENATED MODULE: ./node_modules/is-docker/index.js
@ -2876,7 +2900,7 @@ function isDocker() {
}
;// CONCATENATED MODULE: ./src/index.ts
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
var src_awaiter = (undefined && undefined.__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); } }
@ -2888,7 +2912,7 @@ var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _argume
(() => __awaiter(void 0, void 0, void 0, function* () {
(() => src_awaiter(void 0, void 0, void 0, function* () {
if (process.platform !== "linux") {
console.log(UBUNTU_MESSAGE);
return;
@ -2897,8 +2921,8 @@ var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _argume
console.log(CONTAINER_MESSAGE);
return;
}
if (core.getBooleanInput("disable-telemetry") &&
core.getInput("egress-policy") === "block") {
if (lib_core.getBooleanInput("disable-telemetry") &&
lib_core.getInput("egress-policy") === "block") {
console.log("Telemetry will not be sent to StepSecurity API as disable-telemetry is set to true");
}
else {

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

33
dist/post/index.js vendored
View file

@ -61006,10 +61006,33 @@ var external_child_process_ = __nccwpck_require__(3129);
// EXTERNAL MODULE: ./node_modules/@actions/core/lib/core.js
var lib_core = __nccwpck_require__(2186);
;// CONCATENATED MODULE: ./src/common.ts
var __awaiter = (undefined && undefined.__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());
});
};
function printInfo(web_url) {
console.log("\x1b[32m%s\x1b[0m", "View security insights and recommended policy at:");
console.log(`${web_url}/github/${process.env["GITHUB_REPOSITORY"]}/actions/runs/${process.env["GITHUB_RUN_ID"]}`);
}
function addSummary() {
return __awaiter(this, void 0, void 0, function* () {
if (process.env.STATE_monitorStatusCode === "200") {
const web_url = "https://app.stepsecurity.io";
const insights_url = `${web_url}/github/${process.env["GITHUB_REPOSITORY"]}/actions/runs/${process.env["GITHUB_RUN_ID"]}`;
yield lib_core.summary.addSeparator()
.addImage("https://github.com/step-security/harden-runner/raw/main/images/banner.png", "StepSecurity Harden-Runner", { width: "200" })
.addLink("View security insights and recommended policy", insights_url)
.addSeparator()
.write();
}
});
}
const CONTAINER_MESSAGE = "This job is running in a container. Harden Runner does not run in a container as it needs sudo access to run. This job will not be monitored.";
const UBUNTU_MESSAGE = "This job is not running in a GitHub Actions Hosted Runner Ubuntu VM. Harden Runner is only supported on Ubuntu VM. This job will not be monitored.";
@ -61055,7 +61078,7 @@ var auth = __nccwpck_require__(5526);
// EXTERNAL MODULE: external "crypto"
var external_crypto_ = __nccwpck_require__(6417);
;// CONCATENATED MODULE: ./src/cache.ts
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
var cache_awaiter = (undefined && undefined.__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); } }
@ -61107,7 +61130,7 @@ function getCacheVersion(paths, compressionMethod) {
return crypto.createHash("sha256").update(components.join("|")).digest("hex");
}
function getCacheEntry(keys, paths, options) {
return __awaiter(this, void 0, void 0, function* () {
return cache_awaiter(this, void 0, void 0, function* () {
const httpClient = createHttpClient();
const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod);
const resource = `cache?keys=${encodeURIComponent(keys.join(","))}&version=${version}`;
@ -61232,6 +61255,12 @@ var cleanup_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _
console.log(exception);
}
}
try {
yield addSummary();
}
catch (exception) {
console.log(exception);
}
}))();
function sleep(ms) {
return new Promise((resolve) => {

File diff suppressed because one or more lines are too long

72
dist/pre/index.js vendored
View file

@ -14111,7 +14111,7 @@ var __webpack_exports__ = {};
__nccwpck_require__.r(__webpack_exports__);
// EXTERNAL MODULE: ./node_modules/@actions/core/lib/core.js
var core = __nccwpck_require__(2186);
var lib_core = __nccwpck_require__(2186);
// EXTERNAL MODULE: external "child_process"
var external_child_process_ = __nccwpck_require__(3129);
// EXTERNAL MODULE: external "fs"
@ -14135,10 +14135,34 @@ const stringify = dist.stringify;
const parse = dist.parse;
;// CONCATENATED MODULE: ./src/common.ts
var __awaiter = (undefined && undefined.__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());
});
};
function printInfo(web_url) {
console.log("\x1b[32m%s\x1b[0m", "View security insights and recommended policy at:");
console.log(`${web_url}/github/${process.env["GITHUB_REPOSITORY"]}/actions/runs/${process.env["GITHUB_RUN_ID"]}`);
}
function addSummary() {
return __awaiter(this, void 0, void 0, function* () {
if (process.env.STATE_monitorStatusCode === "200") {
const web_url = "https://app.stepsecurity.io";
const insights_url = `${web_url}/github/${process.env["GITHUB_REPOSITORY"]}/actions/runs/${process.env["GITHUB_RUN_ID"]}`;
yield core.summary
.addSeparator()
.addImage("https://github.com/step-security/harden-runner/raw/main/images/banner.png", "StepSecurity Harden-Runner", { width: "200" })
.addLink("View security insights and recommended policy", insights_url)
.addSeparator()
.write();
}
});
}
const CONTAINER_MESSAGE = "This job is running in a container. Harden Runner does not run in a container as it needs sudo access to run. This job will not be monitored.";
const UBUNTU_MESSAGE = "This job is not running in a GitHub Actions Hosted Runner Ubuntu VM. Harden Runner is only supported on Ubuntu VM. This job will not be monitored.";
@ -14157,9 +14181,9 @@ function verifyChecksum(downloadPath) {
.digest("hex"); // checksum of downloaded file
const expectedChecksum = "79f397360470d6e42c73d6c9c5cf485ac9982e56e3e3fdd07f66c59cda4388c8"; // checksum for v0.12.1
if (checksum !== expectedChecksum) {
core.setFailed(`Checksum verification failed, expected ${expectedChecksum} instead got ${checksum}`);
lib_core.setFailed(`Checksum verification failed, expected ${expectedChecksum} instead got ${checksum}`);
}
core.debug("Checksum verification passed.");
lib_core.debug("Checksum verification passed.");
}
;// CONCATENATED MODULE: external "node:fs"
@ -14197,10 +14221,12 @@ function isDocker() {
// EXTERNAL MODULE: ./node_modules/@actions/github/lib/github.js
var github = __nccwpck_require__(5438);
// EXTERNAL MODULE: external "os"
var external_os_ = __nccwpck_require__(2087);
// EXTERNAL MODULE: ./node_modules/@actions/http-client/lib/auth.js
var auth = __nccwpck_require__(5526);
;// CONCATENATED MODULE: ./src/cache.ts
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
var cache_awaiter = (undefined && undefined.__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); } }
@ -14222,7 +14248,7 @@ function getCacheApiUrl(resource) {
throw new Error("Cache Service Url not found, unable to restore cache.");
}
const url = `${baseUrl}_apis/artifactcache/${resource}`;
core.debug(`Resource Url: ${url}`);
lib_core.debug(`Resource Url: ${url}`);
return url;
}
function createAcceptHeader(type, apiVersion) {
@ -14252,7 +14278,7 @@ function getCacheVersion(paths, compressionMethod) {
return external_crypto_.createHash("sha256").update(components.join("|")).digest("hex");
}
function getCacheEntry(keys, paths, options) {
return __awaiter(this, void 0, void 0, function* () {
return cache_awaiter(this, void 0, void 0, function* () {
const httpClient = createHttpClient();
const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod);
const resource = `cache?keys=${encodeURIComponent(keys.join(","))}&version=${version}`;
@ -14313,6 +14339,7 @@ var setup_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _ar
(() => setup_awaiter(void 0, void 0, void 0, function* () {
try {
if (process.platform !== "linux") {
@ -14334,11 +14361,11 @@ var setup_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _ar
correlation_id: correlation_id,
working_directory: process.env["GITHUB_WORKSPACE"],
api_url: api_url,
allowed_endpoints: core.getInput("allowed-endpoints"),
egress_policy: core.getInput("egress-policy"),
disable_telemetry: core.getBooleanInput("disable-telemetry"),
disable_sudo: core.getBooleanInput("disable-sudo"),
disable_file_monitoring: core.getBooleanInput("disable-file-monitoring"),
allowed_endpoints: lib_core.getInput("allowed-endpoints"),
egress_policy: lib_core.getInput("egress-policy"),
disable_telemetry: lib_core.getBooleanInput("disable-telemetry"),
disable_sudo: lib_core.getBooleanInput("disable-sudo"),
disable_file_monitoring: lib_core.getBooleanInput("disable-file-monitoring"),
private: github.context.payload.repository.private,
};
if (isValidEvent()) {
@ -14347,32 +14374,37 @@ var setup_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _ar
compressionMethod: CompressionMethod.ZstdWithoutLong,
});
const url = new URL(cacheEntry.archiveLocation);
core.info(`Adding cacheHost: ${url.hostname}:443 to allowed-endpoints`);
lib_core.info(`Adding cacheHost: ${url.hostname}:443 to allowed-endpoints`);
confg.allowed_endpoints += ` ${url.hostname}:443`;
}
catch (exception) {
// some exception has occurred.
core.info("Unable to fetch cacheURL");
lib_core.info("Unable to fetch cacheURL");
if (confg.egress_policy === "block") {
core.info("Switching egress-policy to audit mode");
lib_core.info("Switching egress-policy to audit mode");
confg.egress_policy = "audit";
}
}
}
if (confg.egress_policy !== "audit" && confg.egress_policy !== "block") {
core.setFailed("egress-policy must be either audit or block");
lib_core.setFailed("egress-policy must be either audit or block");
}
if (confg.egress_policy === "block" && confg.allowed_endpoints === "") {
core.warning("egress-policy is set to block (default) and allowed-endpoints is empty. No outbound traffic will be allowed for job steps.");
lib_core.warning("egress-policy is set to block (default) and allowed-endpoints is empty. No outbound traffic will be allowed for job steps.");
}
if (confg.disable_telemetry !== true && confg.disable_telemetry !== false) {
core.setFailed("disable-telemetry must be a boolean value");
lib_core.setFailed("disable-telemetry must be a boolean value");
}
if (!confg.disable_telemetry) {
let _http = new lib.HttpClient();
_http.requestOptions = { socketTimeout: 3 * 1000 };
try {
yield _http.get(`${api_url}/github/${process.env["GITHUB_REPOSITORY"]}/actions/runs/${process.env["GITHUB_RUN_ID"]}/monitor`);
const resp = yield _http.get(`${api_url}/github/${process.env["GITHUB_REPOSITORY"]}/actions/runs/${process.env["GITHUB_RUN_ID"]}/monitor`);
if (resp.message.statusCode === 200) {
external_fs_.appendFileSync(process.env.GITHUB_STATE, `monitorStatusCode=${resp.message.statusCode}${external_os_.EOL}`, {
encoding: "utf8",
});
}
}
catch (e) {
console.log(`error in connecting to ${api_url}: ${e}`);
@ -14382,7 +14414,7 @@ var setup_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _ar
external_child_process_.execSync("sudo mkdir -p /home/agent");
external_child_process_.execSync("sudo chown -R $USER /home/agent");
// Note: to avoid github rate limiting
let token = core.getInput("token");
let token = lib_core.getInput("token");
let auth = `token ${token}`;
const downloadPath = yield tool_cache.downloadTool("https://github.com/step-security/agent/releases/download/v0.12.1/agent_0.12.1_linux_amd64.tar.gz", undefined, auth);
verifyChecksum(downloadPath); // NOTE: verifying agent's checksum, before extracting
@ -14429,7 +14461,7 @@ var setup_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _ar
}
}
catch (error) {
core.setFailed(error.message);
lib_core.setFailed(error.message);
}
}))();
function sleep(ms) {

File diff suppressed because one or more lines are too long

5
jest.config.js Normal file
View file

@ -0,0 +1,5 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};

5539
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "step-security-harden-runner",
"version": "2.0.0",
"version": "2.1.0",
"description": "Security agent for GitHub-hosted runner to monitor the build process",
"main": "index.js",
"scripts": {
@ -8,7 +8,8 @@
"main": "ncc build src/index.ts --source-map",
"pre": "ncc build src/setup.ts --source-map -o dist/pre",
"post": "ncc build src/cleanup.ts --source-map -o dist/post",
"lint": "eslint src/**/*.ts"
"lint": "eslint src/**/*.ts",
"test": "jest"
},
"repository": {
"type": "git",
@ -34,16 +35,16 @@
"uuid": "^8.3.2"
},
"devDependencies": {
"@types/jest": "^27.0.1",
"@types/jest": "^27.5.2",
"@types/node": "^16.9.0",
"@typescript-eslint/eslint-plugin": "^4.29.2",
"@typescript-eslint/parser": "^4.29.2",
"@vercel/ncc": "^0.30.0",
"eslint": "^7.32.0",
"eslint-config-google": "^0.14.0",
"jest": ">=27.4.7",
"jest": "^29.3.1",
"jest-junit": ">=13.0.0",
"ts-jest": ">=27.1.3",
"ts-jest": "^29.0.3",
"typescript": "^4.3.5"
}
}

View file

@ -82,6 +82,12 @@ import path from "path";
console.log(exception);
}
}
try {
await common.addSummary();
} catch (exception) {
console.log(exception);
}
})();
function sleep(ms) {

26
src/common.test.ts Normal file
View file

@ -0,0 +1,26 @@
import { addSummary } from "./common";
import * as cp from "child_process";
test("adding stepsecurity summary in github_summary", async () => {
let expected = `<hr>
<img src="https://github.com/step-security/harden-runner/raw/main/images/banner.png" alt="StepSecurity Harden-Runner" width="200">
<a href="https://app.stepsecurity.io/github/step-security/test/actions/runs/12345">View security insights and recommended policy</a>
<hr>
`;
const github_summary = "/tmp/github_summary";
cp.execSync(`touch ${github_summary}`);
process.env["STATE_monitorStatusCode"] = "200";
process.env["GITHUB_STEP_SUMMARY"] = github_summary;
process.env["GITHUB_REPOSITORY"] = "step-security/test";
process.env["GITHUB_RUN_ID"] = "12345";
await addSummary();
let output = cp.execSync(`cat ${github_summary}`).toString();
cp.execSync(`rm ${github_summary}`);
expect(output).toMatch(expected);
});

View file

@ -1,3 +1,4 @@
import * as core from "@actions/core";
export function printInfo(web_url) {
console.log(
"\x1b[32m%s\x1b[0m",
@ -8,7 +9,26 @@ export function printInfo(web_url) {
`${web_url}/github/${process.env["GITHUB_REPOSITORY"]}/actions/runs/${process.env["GITHUB_RUN_ID"]}`
);
}
export async function addSummary() {
if (process.env.STATE_monitorStatusCode === "200") {
const web_url = "https://app.stepsecurity.io";
const insights_url = `${web_url}/github/${process.env["GITHUB_REPOSITORY"]}/actions/runs/${process.env["GITHUB_RUN_ID"]}`;
await core.summary
.addSeparator()
.addImage(
"https://github.com/step-security/harden-runner/raw/main/images/banner.png",
"StepSecurity Harden-Runner",
{ width: "200" }
)
.addLink(
"View security insights and recommended policy",
insights_url
)
.addSeparator()
.write();
}
}
export const CONTAINER_MESSAGE =
"This job is running in a container. Harden Runner does not run in a container as it needs sudo access to run. This job will not be monitored.";

View file

@ -9,6 +9,7 @@ import * as tc from "@actions/tool-cache";
import { verifyChecksum } from "./checksum";
import isDocker from "is-docker";
import { context } from "@actions/github";
import { EOL } from "os";
import {
cacheFile,
cacheKey,
@ -85,9 +86,19 @@ import {
let _http = new httpm.HttpClient();
_http.requestOptions = { socketTimeout: 3 * 1000 };
try {
await _http.get(
const resp: httpm.HttpClientResponse = await _http.get(
`${api_url}/github/${process.env["GITHUB_REPOSITORY"]}/actions/runs/${process.env["GITHUB_RUN_ID"]}/monitor`
);
if(resp.message.statusCode === 200){
fs.appendFileSync(
process.env.GITHUB_STATE,
`monitorStatusCode=${resp.message.statusCode}${EOL}`,
{
encoding: "utf8",
}
);
}
} catch (e) {
console.log(`error in connecting to ${api_url}: ${e}`);
}