Skip to content

Commit d1d5c9b

Browse files
authored
[MH-16215] Add run_number flag to CLI. (#171)
* Upgrade deps. * Update version to 1.0.9. * Update linting settings. * More version updates. * Fixes for linting errors. * Add jest to the dev dependencies; fixes lint error where it couldn't be detected. * Rebuild the distribution. * Consolidate the config into a Config type. * Rename fields. * Add run_number to mayhem download.
1 parent be08534 commit d1d5c9b

6 files changed

Lines changed: 537 additions & 452 deletions

File tree

.eslintrc.json

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,54 @@
11
{
2+
"parser": "@typescript-eslint/parser",
3+
"plugins": [
4+
"@typescript-eslint",
5+
"jest",
6+
"prettier"
7+
],
8+
"extends": [
9+
"eslint:recommended",
10+
"plugin:@typescript-eslint/eslint-recommended",
11+
"plugin:@typescript-eslint/recommended",
12+
"plugin:github/recommended",
13+
"plugin:jest/recommended"
14+
],
215
"env": {
316
"browser": true,
4-
"es2021": true
17+
"es6": true,
18+
"node": true,
19+
"jest": true
520
},
6-
"extends": [
7-
"google", "prettier"
8-
],
9-
"parser": "@typescript-eslint/parser",
1021
"parserOptions": {
11-
"ecmaVersion": 13,
12-
"sourceType": "module"
22+
"ecmaVersion": 2023,
23+
"sourceType": "module",
24+
"project": "./tsconfig.json"
1325
},
14-
"plugins": ["@typescript-eslint", "prettier"],
1526
"rules": {
16-
"prettier/prettier": "error"
27+
// Error for any prettier formatting issues.
28+
"prettier/prettier": "error",
29+
// Error for any if blocks without curly braces.
30+
"curly": "error",
31+
// Error if unused vars or args are present that dont begin with an underscore.
32+
"@typescript-eslint/no-unused-vars": [
33+
"error",
34+
{
35+
"argsIgnorePattern": "^_",
36+
"varsIgnorePattern": "^_"
37+
}
38+
],
39+
"no-unused-vars": [
40+
"error",
41+
{
42+
"argsIgnorePattern": "^_",
43+
"varsIgnorePattern": "^_"
44+
}
45+
],
46+
// Error if any unused expressions are present.
47+
"no-unused-expressions": "error",
48+
// Be explicit about parsing numbers.
49+
"radix": "error",
50+
// Disable broken eslint no-shadow rule for TypeScript enums.
51+
"no-shadow": "off",
52+
"@typescript-eslint/no-shadow": "error"
1753
}
18-
}
54+
}

dist/index.js

Lines changed: 105 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,6 @@ require('./sourcemap-register.js');/******/ (() => { // webpackBootstrap
66

77
"use strict";
88

9-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10-
if (k2 === undefined) k2 = k;
11-
var desc = Object.getOwnPropertyDescriptor(m, k);
12-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13-
desc = { enumerable: true, get: function() { return m[k]; } };
14-
}
15-
Object.defineProperty(o, k2, desc);
16-
}) : (function(o, m, k, k2) {
17-
if (k2 === undefined) k2 = k;
18-
o[k2] = m[k];
19-
}));
20-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21-
Object.defineProperty(o, "default", { enumerable: true, value: v });
22-
}) : function(o, v) {
23-
o["default"] = v;
24-
});
25-
var __importStar = (this && this.__importStar) || function (mod) {
26-
if (mod && mod.__esModule) return mod;
27-
var result = {};
28-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
29-
__setModuleDefault(result, mod);
30-
return result;
31-
};
329
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3310
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3411
return new (P || (P = Promise))(function (resolve, reject) {
@@ -39,116 +16,133 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
3916
});
4017
};
4118
Object.defineProperty(exports, "__esModule", ({ value: true }));
42-
const core = __importStar(__nccwpck_require__(2186));
43-
const exec = __importStar(__nccwpck_require__(1514));
44-
const tc = __importStar(__nccwpck_require__(7784));
19+
const core_1 = __nccwpck_require__(2186);
20+
const exec_1 = __nccwpck_require__(1514);
21+
const tool_cache_1 = __nccwpck_require__(7784);
4522
const fs_1 = __nccwpck_require__(7147);
46-
const mayhemUrl = core.getInput("mayhem-url") || "https://app.mayhem.security";
47-
/** Return local path to donwloaded or cached CLI */
48-
function mcodeCLI() {
23+
const mayhemUrl = (0, core_1.getInput)("mayhem-url") || "https://app.mayhem.security";
24+
/**
25+
* Operating systems that an mCode CLI is available for, mapped to the URL path it can be
26+
* downloaded from on a recent Mayhem cluster.
27+
*/
28+
var CliOsPath;
29+
(function (CliOsPath) {
30+
CliOsPath["Linux"] = "Linux/mayhem";
31+
CliOsPath["MacOS"] = "Darwin/mayhem.pkg";
32+
CliOsPath["Windows"] = "Windows/mayhem.exe";
33+
})(CliOsPath || (CliOsPath = {}));
34+
function getConfig() {
35+
var _a;
36+
const githubToken = (0, core_1.getInput)("github-token", {
37+
required: true,
38+
});
39+
const repo = process.env["GITHUB_REPOSITORY"];
40+
if (repo === undefined) {
41+
throw Error("Missing GITHUB_REPOSITORY environment variable. " +
42+
"Are you not running this in a Github Action environment?");
43+
}
44+
const ghRepo = `${process.env["GITHUB_SERVER_URL"]}:443/${repo}/`;
45+
const eventPath = process.env["GITHUB_EVENT_PATH"] || "event.json";
46+
const event = JSON.parse((0, fs_1.readFileSync)(eventPath, "utf-8")) || {};
47+
const eventPullRequest = event.pull_request;
48+
return {
49+
githubToken,
50+
mayhemToken: (0, core_1.getInput)("mayhem-token") || githubToken,
51+
packagePath: (0, core_1.getInput)("package") || ".",
52+
sarifOutputDir: (0, core_1.getInput)("sarif-output") || "",
53+
junitOutputDir: (0, core_1.getInput)("junit-output") || "",
54+
coverageOutputDir: (0, core_1.getInput)("coverage-output") || "",
55+
failOnDefects: (0, core_1.getBooleanInput)("fail-on-defects") || false,
56+
verbosity: (0, core_1.getInput)("verbosity") || "info",
57+
owner: (0, core_1.getInput)("owner").toLowerCase(),
58+
project: ((0, core_1.getInput)("project") || repo).toLowerCase(),
59+
repo,
60+
ciUrl: `${ghRepo}/actions/runs/${process.env["GITHUB_RUN_ID"]}`,
61+
branchName: eventPullRequest
62+
? eventPullRequest.head.ref
63+
: ((_a = process.env["GITHUB_REF_NAME"]) === null || _a === void 0 ? void 0 : _a.slice("refs/heads/".length)) || "main",
64+
revision: eventPullRequest
65+
? eventPullRequest.head.sha
66+
: process.env["GITHUB_SHA"] || "unknown",
67+
mergeBaseBranchName: eventPullRequest ? eventPullRequest.base.ref : "main",
68+
};
69+
}
70+
/**
71+
* Downloads the mCode CLI from the given Mayhem cluster, marks it as executable, and returns the
72+
* path to the downloaded CLI.
73+
* @param url the base URL of the Mayhem cluster, such as "https://app.mayhem.security".
74+
* @param os the operating system to download the CLI for.
75+
* @return Path to the downloaded mCode CLI; resolves when the CLI download is complete.
76+
*/
77+
function downloadCli(url, os) {
4978
return __awaiter(this, void 0, void 0, function* () {
50-
// Get latest version from API
51-
const os = "Linux";
52-
const bin = "mayhem";
53-
// Download the CLI and cache it if version is set
54-
const mcodePath = yield tc.downloadTool(`${mayhemUrl}/cli/${os}/${bin}`);
79+
// Download the CLI and mark it as executable.
80+
const mcodePath = yield (0, tool_cache_1.downloadTool)(`${url}/cli/${os}`);
5581
(0, fs_1.chmodSync)(mcodePath, 0o755);
56-
// const folder = await tc.cacheFile(mcodePath, bin, bin, cliVersion, os);
57-
// return `${folder}/${bin}`;
5882
return mcodePath;
5983
});
6084
}
6185
/** Mapping action arguments to CLI arguments and completing a run */
6286
function run() {
6387
return __awaiter(this, void 0, void 0, function* () {
64-
var _a;
6588
try {
66-
const cli = yield mcodeCLI();
67-
// Load inputs
68-
const githubToken = core.getInput("github-token", {
69-
required: true,
70-
});
71-
const mayhemToken = core.getInput("mayhem-token") || githubToken;
72-
const packagePath = core.getInput("package") || ".";
73-
const sarifOutput = core.getInput("sarif-output") || "";
74-
const junitOutput = core.getInput("junit-output") || "";
75-
const coverageOutput = core.getInput("coverage-output") || "";
76-
const failOnDefects = core.getBooleanInput("fail-on-defects") || false;
77-
const verbosity = core.getInput("verbosity") || "info";
78-
const owner = core.getInput("owner").toLowerCase();
79-
const args = (core.getInput("args") || "").split(" ");
89+
// Validate the action inputs and create a Config object from them.
90+
const config = getConfig();
91+
// Download the mCode CLI for Linux.
92+
const cli = yield downloadCli(mayhemUrl, CliOsPath.Linux);
93+
const args = ((0, core_1.getInput)("args") || "").split(" ");
8094
// defaults next
8195
if (!args.includes("--duration")) {
8296
args.push("--duration", "60");
8397
}
8498
if (!args.includes("--image")) {
8599
args.push("--image", "forallsecure/debian-buster:latest");
86100
}
87-
const repo = process.env["GITHUB_REPOSITORY"];
88-
if (repo === undefined) {
89-
throw Error("Missing GITHUB_REPOSITORY environment variable. " +
90-
"Are you not running this in a Github Action environment?");
91-
}
92-
const project = (core.getInput("project") || repo).toLowerCase();
93-
const eventPath = process.env["GITHUB_EVENT_PATH"] || "event.json";
94-
const event = JSON.parse((0, fs_1.readFileSync)(eventPath, "utf-8")) || {};
95-
const eventPullRequest = event.pull_request;
96-
const ghRepo = `${process.env["GITHUB_SERVER_URL"]}:443/${repo}/`;
97-
const ciUrl = `${ghRepo}/actions/runs/${process.env["GITHUB_RUN_ID"]}`;
98-
const branchName = eventPullRequest
99-
? eventPullRequest.head.ref
100-
: ((_a = process.env["GITHUB_REF_NAME"]) === null || _a === void 0 ? void 0 : _a.slice("refs/heads/".length)) || "main";
101-
const revision = eventPullRequest
102-
? eventPullRequest.head.sha
103-
: process.env["GITHUB_SHA"] || "unknown";
104-
const mergeBaseBranchName = eventPullRequest
105-
? eventPullRequest.base.ref
106-
: "main";
107-
args.push("--ci-url", ciUrl);
108-
args.push("--merge-base-branch-name", mergeBaseBranchName);
109-
args.push("--branch-name", branchName);
110-
args.push("--revision", revision);
101+
args.push("--ci-url", config.ciUrl);
102+
args.push("--merge-base-branch-name", config.mergeBaseBranchName);
103+
args.push("--branch-name", config.branchName);
104+
args.push("--revision", config.revision);
111105
const argsString = args.join(" ");
112106
// Generate arguments for wait command
113107
// sarif, junit, coverage
114108
const waitArgs = [];
115-
if (sarifOutput) {
109+
if (config.sarifOutputDir) {
116110
// $runName is a variable that is set in the bash script
117-
waitArgs.push("--sarif", `${sarifOutput}/\${runName}.sarif`);
111+
waitArgs.push("--sarif", `${config.sarifOutputDir}/\${runName}.sarif`);
118112
}
119-
if (junitOutput) {
113+
if (config.junitOutputDir) {
120114
// $runName is a variable that is set in the bash script
121-
waitArgs.push("--junit", `${junitOutput}/\${runName}.xml`);
115+
waitArgs.push("--junit", `${config.junitOutputDir}/\${runName}.xml`);
122116
}
123-
if (coverageOutput) {
117+
if (config.coverageOutputDir) {
124118
waitArgs.push("--coverage");
125119
}
126-
if (failOnDefects) {
120+
if (config.failOnDefects) {
127121
waitArgs.push("--fail-on-defects");
128122
}
129123
// create wait args string
130124
const waitArgsString = waitArgs.join(" ");
131125
const script = `
132126
set -xe
133127
# create sarif output directory
134-
if [ -n "${sarifOutput}" ]; then
135-
mkdir -p ${sarifOutput};
128+
if [ -n "${config.sarifOutputDir}" ]; then
129+
mkdir -p ${config.sarifOutputDir};
136130
fi
137131

138132
# create junit output directory
139-
if [ -n "${junitOutput}" ]; then
140-
mkdir -p ${junitOutput};
133+
if [ -n "${config.junitOutputDir}" ]; then
134+
mkdir -p ${config.junitOutputDir};
141135
fi
142136

143137
# create coverage output directory
144-
if [ -n "${coverageOutput}" ]; then
145-
mkdir -p ${coverageOutput};
138+
if [ -n "${config.coverageOutputDir}" ]; then
139+
mkdir -p ${config.coverageOutputDir};
146140
fi
147141

148142
# Run mayhem
149-
run=$(${cli} --verbosity ${verbosity} run ${packagePath} \
150-
--project ${project} \
151-
--owner ${owner} ${argsString});
143+
run=$(${cli} --verbosity ${config.verbosity} run ${config.packagePath} \
144+
--project ${config.project} \
145+
--owner ${config.owner} ${argsString});
152146

153147
# Persist the run id to the GitHub output
154148
echo "runId=$run" >> $GITHUB_OUTPUT;
@@ -161,10 +155,10 @@ function run() {
161155
fi
162156

163157
# if the user didn't specify requiring any output, don't wait for the result.
164-
if [ -z "${coverageOutput}" ] && \
165-
[ -z "${junitOutput}" ] && \
166-
[ -z "${sarifOutput}" ] && \
167-
[ "${failOnDefects.toString().toLowerCase()}" != "true" ]; then
158+
if [ -z "${config.coverageOutputDir}" ] && \
159+
[ -z "${config.junitOutputDir}" ] && \
160+
[ -z "${config.sarifOutputDir}" ] && \
161+
[ "${config.failOnDefects.toString().toLowerCase()}" != "true" ]; then
168162
echo "No coverage, junit or sarif output requested, not waiting for job result.";
169163
exit 0;
170164
fi
@@ -173,55 +167,56 @@ function run() {
173167
runName="$(echo $run | awk -F / '{ print $(NF-1) }')";
174168

175169
# wait for run to finish
176-
if ! ${cli} --verbosity ${verbosity} wait $run \
177-
--owner ${owner} \
170+
if ! ${cli} --verbosity ${config.verbosity} wait $run \
171+
--owner ${config.owner} \
178172
${waitArgsString}; then
179173
exit 3;
180174
fi
181175

182176

183177
# check status, exit with non-zero status if failed or stopped
184-
status=$(${cli} --verbosity ${verbosity} show \
185-
--owner ${owner} \
178+
status=$(${cli} --verbosity ${config.verbosity} show \
179+
--owner ${config.owner} \
186180
--format json $run | jq '.[0].status');
187181
if [[ $status == *"stopped"* || $status == *"failed"* ]]; then
188182
exit 2;
189183
fi
190184

191-
# Strip the run number from the full run path to get the project/target.
185+
# Strip the run number from the full run path to get the project/target,
186+
# and save the run number separately.
192187
target=$(echo $run | sed 's:/[^/]*$::')
188+
run_number=$(echo $run | sed 's:.*/::')
193189

194-
if [ -n "${coverageOutput}" ]; then
195-
${cli} --verbosity ${verbosity} download --owner ${owner} $target -o ${coverageOutput};
190+
if [ -n "${config.coverageOutputDir}" ]; then
191+
${cli} --verbosity ${config.verbosity} download --owner ${config.owner} --output ${config.coverageOutputDir} --run_number $run_number $target;
196192
fi
197193
`;
198-
process.env["MAYHEM_TOKEN"] = mayhemToken;
194+
process.env["MAYHEM_TOKEN"] = config.mayhemToken;
199195
process.env["MAYHEM_URL"] = mayhemUrl;
200-
process.env["MAYHEM_PROJECT"] = repo;
196+
process.env["MAYHEM_PROJECT"] = config.repo;
201197
// Start fuzzing
202-
const cliRunning = exec.exec("bash", ["-c", script], {
198+
const cliRunning = (0, exec_1.exec)("bash", ["-c", script], {
203199
ignoreReturnCode: true,
204200
});
205201
const res = yield cliRunning;
206-
if (res == 1) {
207-
/* eslint-disable max-len */
202+
if (res === 1) {
208203
throw new Error(`The Mayhem for Code scan was unable to execute the Mayhem run for your target.
209204
Check your configuration. For package visibility/permissions issues, see
210205
https://docs.github.com/en/packages/learn-github-packages/configuring-a-packages-access-control-and-visibility
211206
on how to set your package to 'Public'.`);
212207
}
213-
else if (res == 2) {
208+
else if (res === 2) {
214209
throw new Error("The Mayhem for Code scan detected the Mayhem run for your " +
215210
"target was unsuccessful.");
216211
}
217-
else if (res == 3) {
212+
else if (res === 3) {
218213
throw new Error("The Mayhem for Code scan found defects in your target.");
219214
}
220215
}
221216
catch (err) {
222217
if (err instanceof Error) {
223-
core.info(`mcode action failed with: ${err.message}`);
224-
core.setFailed(err.message);
218+
(0, core_1.info)(`mcode action failed with: ${err.message}`);
219+
(0, core_1.setFailed)(err.message);
225220
}
226221
}
227222
});

dist/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)