Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,55 @@ jobs:
run: |
[ -z "${{ steps.mcode-action.outputs.runId }}" ] && echo "runId was blank!" && exit 1;
echo "The run id was: ${{ steps.mcode-action.outputs.runId }}"

# End-to-end check for the run revision, dispatch only (it starts a real Mayhem run): check out a
# commit that differs from GITHUB_SHA (the parent of the dispatched ref), run the action against it,
# and assert the run Mayhem records carries that checked-out commit — the reusable-workflow case where
# the fuzzed commit is not the one the workflow run started from.
# gh workflow run main.yml --ref <branch>
revision-from-checkout:
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 2

- name: Second checkout at the parent commit (differs from GITHUB_SHA)
id: pkg
run: |
parent="$(git rev-parse HEAD~1)"
git clone -q . pkg
git -C pkg checkout -q "$parent"
echo "parent=$parent" >> "$GITHUB_OUTPUT"
echo "GITHUB_SHA=$GITHUB_SHA"
echo "package checkout=$parent"

- uses: ./
id: mcode-action
with:
mayhem-url: ${{ secrets.MAYHEM_URL }}
mayhem-token: ${{ secrets.MAYHEM_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
package: pkg/__tests__/lighttpd
args: --image forallsecure/lighttpd:vulnerable --duration 30

- name: The run must record the checked-out commit, not GITHUB_SHA
env:
MAYHEM_TOKEN: ${{ secrets.MAYHEM_TOKEN }}
MAYHEM_URL: ${{ secrets.MAYHEM_URL }}
RUN_ID: ${{ steps.mcode-action.outputs.runId }}
PARENT: ${{ steps.pkg.outputs.parent }}
run: |
set -eu
base="${MAYHEM_URL:-https://app.mayhem.security}"
IFS=/ read -r owner project target number <<< "$RUN_ID"
url="$base/api/v2/owner/$owner/project/$project/target/$target/run/$number"
rev="$(curl -sf -H "X-Mayhem-Token: token $MAYHEM_TOKEN" "$url" | jq -r .revision)"
echo "run : $RUN_ID"
echo "recorded : $rev"
echo "checked out : $PARENT"
echo "GITHUB_SHA : $GITHUB_SHA"
[ "$rev" = "$PARENT" ] || { echo "FAIL: the run does not carry the checked-out commit"; exit 1; }
[ "$rev" != "$GITHUB_SHA" ] || { echo "FAIL: the run still carries GITHUB_SHA"; exit 1; }
echo "OK: the run records the commit that was actually checked out"
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ The mCode Action accepts the following inputs:
| | `mayhem-token` | string | Mayhem for Code account token. **Only required within** `mayhem.yml` **if overriding** `mayhem-url`. |
| | `duration` | number | Duration of the run in seconds. Takes precedence over any `--duration` passed via `args`. | 60 |
| | `args` | string | Additional CLI override [arguments](https://app.mayhem.security/docs/code-testing/reference/mayhem-cli-commands/#run) such as specifying the `--testsuite` directory path for a seed test suite. |
| | `revision` | string | Commit recorded on the run. Set it when the job builds a commit other than the one it checked out. | Pull request head on `pull_request` events, else the commit checked out under `package`, else `GITHUB_SHA` |
| | `sarif-output` | string | Path for generating a SARIF report output file. |
| | `junit-output` | string | Path for generating a jUnit report output file. |
| | `coverage-output` | string | Path for generating a coverage report output files. |
Expand Down
73 changes: 73 additions & 0 deletions __tests__/revision.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { execFileSync } from "child_process";
import { mkdtempSync } from "fs";
import { tmpdir } from "os";
import { join } from "path";
import { checkedOutRevision, resolveRevision } from "../src/revision";

const SHA_A = "a".repeat(40);
const SHA_B = "b".repeat(40);
const SHA_C = "c".repeat(40);

describe("resolveRevision", () => {
test("an explicit revision input wins over everything", () => {
expect(resolveRevision("deadbeef", SHA_A, SHA_B, SHA_C)).toEqual({
revision: "deadbeef",
source: "revision input",
});
});

test("a pull request's head commit wins over the checkout and GITHUB_SHA", () => {
expect(resolveRevision("", SHA_A, SHA_B, SHA_C)).toEqual({
revision: SHA_A,
source: "pull request head",
});
});

test("the checked-out HEAD wins over GITHUB_SHA (a reusable workflow called with a rebased commit)", () => {
expect(resolveRevision("", undefined, SHA_B, SHA_C)).toEqual({
revision: SHA_B,
source: "checked-out HEAD",
});
});

test("GITHUB_SHA is the fallback when nothing is checked out", () => {
expect(resolveRevision("", undefined, undefined, SHA_C)).toEqual({
revision: SHA_C,
source: "GITHUB_SHA",
});
});

test("'unknown' when there is nothing at all", () => {
expect(resolveRevision("", undefined, undefined, undefined)).toEqual({
revision: "unknown",
source: "unknown",
});
});
});

describe("checkedOutRevision", () => {
test("reads HEAD of the checkout it is pointed at", () => {
const head = execFileSync("git", ["rev-parse", "HEAD"], {
cwd: join(__dirname, ".."),
})
.toString()
.trim();
expect(checkedOutRevision(join(__dirname, ".."))).toEqual({ sha: head });
});

test("outside a git checkout it reports git's own error instead of swallowing it", () => {
const r = checkedOutRevision(mkdtempSync(join(tmpdir(), "no-git-")));
expect(r.sha).toBeUndefined();
expect(r.error).toMatch(
/git rev-parse HEAD in .* failed: .*not a git repository/i,
);
});

test("a directory that does not exist is an error too, never a throw", () => {
const r = checkedOutRevision("/nonexistent/path");
expect(r.sha).toBeUndefined();
expect(r.error).toMatch(
/^git rev-parse HEAD in '\/nonexistent\/path' failed: /,
);
});
});
6 changes: 6 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ inputs:
duration:
description: Duration of the run in seconds. Takes precedence over any '--duration' passed via 'args'. Defaults to 60 seconds if neither is set.
required: false
revision:
description: >-
Commit to record on the Mayhem run. Defaults to the pull request head on pull_request events, else the
commit checked out under 'package', else GITHUB_SHA. Set it when the job builds a commit other than the
one it checked out.
required: false
args:
description: Command line arguments to override CLI behavior
required: false
Expand Down
Loading
Loading