Skip to content

Commit

Permalink
mock @actions/core
Browse files Browse the repository at this point in the history
  • Loading branch information
shogo82148 committed Sep 22, 2023
1 parent d30e39e commit 8d9dafb
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 106 deletions.
1 change: 1 addition & 0 deletions action/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/node_modules/
/lib/
/dummy.log
32 changes: 24 additions & 8 deletions action/__test__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as fs from "fs";
import * as path from "path";
import * as exec from "@actions/exec";
import * as io from "@actions/io";
import * as core from "@actions/core";
import * as child_process from "child_process";
import * as index from "../src/index";

Expand All @@ -13,13 +14,17 @@ jest.mock("@actions/core");
// extension of executable files
const binExt = os.platform() === "win32" ? ".exe" : "";

process.env.GITHUB_REPOSITORY = "shogo82148/actions-aws-assume-role";
process.env.GITHUB_REPOSITORY = "fuller-inc/actions-aws-assume-role";
process.env.GITHUB_WORKFLOW = "test";
process.env.GITHUB_RUN_ID = "1234567890";
process.env.GITHUB_ACTOR = "shogo82148";
process.env.GITHUB_ACTOR = "fuller-inc";
process.env.GITHUB_SHA = "e3a45c6c16c1464826b36a598ff39e6cc98c4da4";
process.env.GITHUB_REF = "ref/heads/main";

// set dummy id token endpoint
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = "dummy";
process.env.ACTIONS_ID_TOKEN_REQUEST_URL = "https://example.com";

describe("tests", () => {
let tmpdir = "";
let subprocess: child_process.ChildProcess;
Expand Down Expand Up @@ -52,6 +57,8 @@ describe("tests", () => {
});

it("succeed", async () => {
(core.getIDToken as jest.Mock).mockResolvedValueOnce("dummyGitHubIDToken");

await index.assumeRole({
githubToken: "ghs_dummyGitHubToken",
awsRegion: "us-east-1",
Expand All @@ -63,15 +70,24 @@ describe("tests", () => {
useNodeId: false,
obfuscateRepository: "",
});
expect(process.env.AWS_ACCESS_KEY_ID).toBe("AKIAIOSFODNN7EXAMPLE");
expect(process.env.AWS_SECRET_ACCESS_KEY).toBe("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
expect(process.env.AWS_SESSION_TOKEN).toBe("session-token");
expect(process.env.AWS_DEFAULT_REGION).toBe("us-east-1");
expect(process.env.AWS_REGION).toBe("us-east-1");

const exportVariable = core.exportVariable as jest.Mock;
expect(exportVariable).toHaveBeenCalledWith("AWS_ACCESS_KEY_ID", "AKIAIOSFODNN7EXAMPLE");
expect(exportVariable).toHaveBeenCalledWith("AWS_SECRET_ACCESS_KEY", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
expect(exportVariable).toHaveBeenCalledWith("AWS_SESSION_TOKEN", "session-token");
expect(exportVariable).toHaveBeenCalledWith("AWS_DEFAULT_REGION", "us-east-1");
expect(exportVariable).toHaveBeenCalledWith("AWS_REGION", "us-east-1");

const setSecret = core.setSecret as jest.Mock;
expect(setSecret).toHaveBeenCalledWith("AKIAIOSFODNN7EXAMPLE");
expect(setSecret).toHaveBeenCalledWith("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
expect(setSecret).toHaveBeenCalledWith("session-token");
});

it("invalid GitHub Token", async () => {
it("invalid GitHub ID Token", async () => {
await expect(async () => {
(core.getIDToken as jest.Mock).mockResolvedValueOnce("invalid");

await index.assumeRole({
githubToken: "ghp_dummyPersonalGitHubToken",
awsRegion: "us-east-1",
Expand Down
70 changes: 35 additions & 35 deletions action/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as core from '@actions/core';
import * as http from '@actions/http-client';
import * as core from "@actions/core";
import * as http from "@actions/http-client";

interface AssumeRoleParams {
githubToken: string;
Expand Down Expand Up @@ -45,47 +45,47 @@ interface AssumeRoleError {

function validateGitHubToken(token: string) {
if (token.length < 4) {
throw new Error('GITHUB_TOKEN has invalid format');
throw new Error("GITHUB_TOKEN has invalid format");
}
switch (token.substring(0, 4)) {
case 'ghp_':
case "ghp_":
// Personal Access Tokens
throw new Error(
'GITHUB_TOKEN looks like Personal Access Token. `github-token` must be `${{ github.token }}` or `${{ secrets.GITHUB_TOKEN }}`.'
"GITHUB_TOKEN looks like Personal Access Token. `github-token` must be `${{ github.token }}` or `${{ secrets.GITHUB_TOKEN }}`.",
);

case 'gho_':
case "gho_":
// OAuth Access tokens
throw new Error(
'GITHUB_TOKEN looks like OAuth Access token. `github-token` must be `${{ github.token }}` or `${{ secrets.GITHUB_TOKEN }}`.'
"GITHUB_TOKEN looks like OAuth Access token. `github-token` must be `${{ github.token }}` or `${{ secrets.GITHUB_TOKEN }}`.",
);

case 'ghu_':
case "ghu_":
// GitHub App user-to-server tokens
throw new Error(
'GITHUB_TOKEN looks like GitHub App user-to-server token. `github-token` must be `${{ github.token }}` or `${{ secrets.GITHUB_TOKEN }}`.'
"GITHUB_TOKEN looks like GitHub App user-to-server token. `github-token` must be `${{ github.token }}` or `${{ secrets.GITHUB_TOKEN }}`.",
);

case 'ghs_':
case "ghs_":
// GitHub App server-to-server tokens
return; // it's OK

case 'ghr_':
case "ghr_":
throw new Error(
'GITHUB_TOKEN looks like GitHub App refresh token. `github-token` must be `${{ github.token }}` or `${{ secrets.GITHUB_TOKEN }}`.'
"GITHUB_TOKEN looks like GitHub App refresh token. `github-token` must be `${{ github.token }}` or `${{ secrets.GITHUB_TOKEN }}`.",
);
}
// maybe Old Format Personal Access Tokens
throw new Error(
'GITHUB_TOKEN looks like Personal Access Token. `github-token` must be `${{ github.token }}` or `${{ secrets.GITHUB_TOKEN }}`.'
"GITHUB_TOKEN looks like Personal Access Token. `github-token` must be `${{ github.token }}` or `${{ secrets.GITHUB_TOKEN }}`.",
);
}

// comes from the article "AWS federation comes to GitHub Actions"
// https://awsteele.com/blog/2021/09/15/aws-federation-comes-to-github-actions.html
function isIdTokenAvailable(): boolean {
const token = process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN'];
const url = process.env['ACTIONS_ID_TOKEN_REQUEST_URL'];
const token = process.env["ACTIONS_ID_TOKEN_REQUEST_TOKEN"];
const url = process.env["ACTIONS_ID_TOKEN_REQUEST_URL"];
return token && url ? true : false;
}

Expand All @@ -96,14 +96,14 @@ function assertIsDefined<T>(val: T): asserts val is NonNullable<T> {
}

export async function assumeRole(params: AssumeRoleParams) {
const {GITHUB_REPOSITORY, GITHUB_WORKFLOW, GITHUB_RUN_ID, GITHUB_ACTOR, GITHUB_SHA, GITHUB_REF} = process.env;
const { GITHUB_REPOSITORY, GITHUB_WORKFLOW, GITHUB_RUN_ID, GITHUB_ACTOR, GITHUB_SHA, GITHUB_REF } = process.env;
assertIsDefined(GITHUB_REPOSITORY);
assertIsDefined(GITHUB_WORKFLOW);
assertIsDefined(GITHUB_RUN_ID);
assertIsDefined(GITHUB_ACTOR);
assertIsDefined(GITHUB_SHA);
validateGitHubToken(params.githubToken);
const GITHUB_API_URL = process.env['GITHUB_API_URL'] || 'https://api.github.com';
const GITHUB_API_URL = process.env["GITHUB_API_URL"] || "https://api.github.com";

let idToken: string | undefined;
if (isIdTokenAvailable()) {
Expand All @@ -125,9 +125,9 @@ export async function assumeRole(params: AssumeRoleParams) {
run_id: GITHUB_RUN_ID,
workflow: GITHUB_WORKFLOW,
actor: GITHUB_ACTOR,
branch: GITHUB_REF || ''
branch: GITHUB_REF || "",
};
const client = new http.HttpClient('actions-aws-assume-role');
const client = new http.HttpClient("actions-aws-assume-role");
const result = await client.postJson<AssumeRoleResult | AssumeRoleError>(params.providerEndpoint, payload);
if (result.statusCode !== 200) {
const resp = result.result as AssumeRoleError;
Expand All @@ -145,33 +145,33 @@ export async function assumeRole(params: AssumeRoleParams) {
}

core.setSecret(resp.access_key_id);
core.exportVariable('AWS_ACCESS_KEY_ID', resp.access_key_id);
core.exportVariable("AWS_ACCESS_KEY_ID", resp.access_key_id);

core.setSecret(resp.secret_access_key);
core.exportVariable('AWS_SECRET_ACCESS_KEY', resp.secret_access_key);
core.exportVariable("AWS_SECRET_ACCESS_KEY", resp.secret_access_key);

core.setSecret(resp.session_token);
core.exportVariable('AWS_SESSION_TOKEN', resp.session_token);
core.exportVariable("AWS_SESSION_TOKEN", resp.session_token);

core.exportVariable('AWS_DEFAULT_REGION', params.awsRegion);
core.exportVariable('AWS_REGION', params.awsRegion);
core.exportVariable("AWS_DEFAULT_REGION", params.awsRegion);
core.exportVariable("AWS_REGION", params.awsRegion);
}

async function run() {
try {
const required = {
required: true
required: true,
};
const githubToken = core.getInput('github-token', required);
const awsRegion = core.getInput('aws-region', required);
const roleToAssume = core.getInput('role-to-assume', required);
const roleDurationSeconds = Number.parseInt(core.getInput('role-duration-seconds', required));
const roleSessionName = core.getInput('role-session-name', required);
const roleSessionTagging = core.getBooleanInput('role-session-tagging', required);
const githubToken = core.getInput("github-token", required);
const awsRegion = core.getInput("aws-region", required);
const roleToAssume = core.getInput("role-to-assume", required);
const roleDurationSeconds = Number.parseInt(core.getInput("role-duration-seconds", required));
const roleSessionName = core.getInput("role-session-name", required);
const roleSessionTagging = core.getBooleanInput("role-session-tagging", required);
const providerEndpoint =
core.getInput('provider-endpoint') || 'https://uw4qs7ndjj.execute-api.us-east-1.amazonaws.com/assume-role';
const useNodeId = core.getBooleanInput('use-node-id', required);
const obfuscateRepository = core.getInput('obfuscate-repository');
core.getInput("provider-endpoint") || "https://uw4qs7ndjj.execute-api.us-east-1.amazonaws.com/assume-role";
const useNodeId = core.getBooleanInput("use-node-id", required);
const obfuscateRepository = core.getInput("obfuscate-repository");
if (roleDurationSeconds <= 0 || roleDurationSeconds > 60 * 60) {
core.setFailed(`invalid role-duration-seconds ${roleDurationSeconds}, it should be from 1 to 3600`);
}
Expand All @@ -184,7 +184,7 @@ async function run() {
roleSessionTagging,
providerEndpoint,
useNodeId,
obfuscateRepository
obfuscateRepository,
});
} catch (error) {
if (error instanceof Error) {
Expand Down
Loading

0 comments on commit 8d9dafb

Please sign in to comment.