Merge pull request #23 from crazy-max/github-print-art

github: printActionsRuntimeTokenACs
This commit is contained in:
CrazyMax
2023-02-03 13:22:41 +01:00
committed by GitHub
5 changed files with 110 additions and 28 deletions

View File

@@ -50,7 +50,7 @@ afterEach(() => {
rimraf.sync(tmpDir);
});
describe('getBuildImageID', () => {
describe('resolveBuildImageID', () => {
it('matches', async () => {
const buildx = new Buildx({
context: new Context()
@@ -58,31 +58,31 @@ describe('getBuildImageID', () => {
const imageID = 'sha256:bfb45ab72e46908183546477a08f8867fc40cebadd00af54b071b097aed127a9';
const imageIDFile = buildx.inputs.getBuildImageIDFilePath();
await fs.writeFileSync(imageIDFile, imageID);
const expected = buildx.inputs.getBuildImageID();
const expected = buildx.inputs.resolveBuildImageID();
expect(expected).toEqual(imageID);
});
});
describe('getBuildMetadata', () => {
describe('resolveBuildMetadata', () => {
it('matches', async () => {
const buildx = new Buildx({
context: new Context()
});
const metadataFile = buildx.inputs.getBuildMetadataFilePath();
await fs.writeFileSync(metadataFile, metadata);
const expected = buildx.inputs.getBuildMetadata();
const expected = buildx.inputs.resolveBuildMetadata();
expect(expected).toEqual(metadata);
});
});
describe('getDigest', () => {
describe('resolveDigest', () => {
it('matches', async () => {
const buildx = new Buildx({
context: new Context()
});
const metadataFile = buildx.inputs.getBuildMetadataFilePath();
await fs.writeFileSync(metadataFile, metadata);
const expected = buildx.inputs.getDigest();
const expected = buildx.inputs.resolveDigest();
expect(expected).toEqual('sha256:b09b9482c72371486bb2c1d2c2a2633ed1d0b8389e12c8d52b9e052725c0c83c');
});
});
@@ -136,7 +136,7 @@ describe('getProvenanceInput', () => {
});
});
describe('getProvenanceAttrs', () => {
describe('resolveProvenanceAttrs', () => {
// prettier-ignore
test.each([
[
@@ -163,11 +163,11 @@ describe('getProvenanceAttrs', () => {
const buildx = new Buildx({
context: new Context()
});
expect(buildx.inputs.getProvenanceAttrs(input)).toEqual(expected);
expect(buildx.inputs.resolveProvenanceAttrs(input)).toEqual(expected);
});
});
describe('generateBuildSecret', () => {
describe('resolveBuildSecret', () => {
test.each([
['A_SECRET=abcdef0123456789', false, 'A_SECRET', 'abcdef0123456789', null],
['GIT_AUTH_TOKEN=abcdefghijklmno=0123456789', false, 'GIT_AUTH_TOKEN', 'abcdefghijklmno=0123456789', null],
@@ -184,9 +184,9 @@ describe('generateBuildSecret', () => {
});
let secret: string;
if (file) {
secret = buildx.inputs.generateBuildSecretFile(kvp);
secret = buildx.inputs.resolveBuildSecretFile(kvp);
} else {
secret = buildx.inputs.generateBuildSecretString(kvp);
secret = buildx.inputs.resolveBuildSecretString(kvp);
}
expect(secret).toEqual(`id=${exKey},src=${tmpName}`);
expect(fs.readFileSync(tmpName, 'utf-8')).toEqual(exValue);

View File

@@ -17,6 +17,7 @@
import {describe, expect, jest, it, beforeEach, afterEach} from '@jest/globals';
import * as fs from 'fs';
import * as path from 'path';
import * as core from '@actions/core';
import {GitHub} from '../src/github';
import {GitHubRepo} from '../src/types/github';
@@ -101,12 +102,52 @@ describe('actionsRuntimeToken', () => {
});
it('empty', async () => {
process.env.ACTIONS_RUNTIME_TOKEN = '';
expect(GitHub.actionsRuntimeToken).toEqual({});
expect(GitHub.actionsRuntimeToken).toBeUndefined();
});
it('malformed', async () => {
process.env.ACTIONS_RUNTIME_TOKEN = 'foo';
expect(() => {
GitHub.actionsRuntimeToken;
}).toThrowError();
});
it('fixture', async () => {
process.env.ACTIONS_RUNTIME_TOKEN = fs.readFileSync(path.join(__dirname, 'fixtures', 'runtimeToken.txt')).toString().trim();
const runtimeToken = GitHub.actionsRuntimeToken;
expect(runtimeToken.ac).toEqual('[{"Scope":"refs/heads/master","Permission":3}]');
expect(runtimeToken.iss).toEqual('vstoken.actions.githubusercontent.com');
expect(runtimeToken?.ac).toEqual('[{"Scope":"refs/heads/master","Permission":3}]');
expect(runtimeToken?.iss).toEqual('vstoken.actions.githubusercontent.com');
});
});
describe('printActionsRuntimeTokenACs', () => {
const originalEnv = process.env;
beforeEach(() => {
jest.resetModules();
process.env = {
...originalEnv
};
});
afterEach(() => {
process.env = originalEnv;
});
it('empty', async () => {
const warnSpy = jest.spyOn(core, 'warning');
process.env.ACTIONS_RUNTIME_TOKEN = '';
await GitHub.printActionsRuntimeTokenACs();
expect(warnSpy).toHaveBeenCalledTimes(1);
expect(warnSpy).toHaveBeenCalledWith(`ACTIONS_RUNTIME_TOKEN not set`);
});
it('malformed', async () => {
const warnSpy = jest.spyOn(core, 'warning');
process.env.ACTIONS_RUNTIME_TOKEN = 'foo';
await GitHub.printActionsRuntimeTokenACs();
expect(warnSpy).toHaveBeenCalledTimes(1);
expect(warnSpy).toHaveBeenCalledWith(`Cannot parse Actions Runtime Token: Invalid token specified: Cannot read properties of undefined (reading 'replace')`);
});
it('refs/heads/master', async () => {
const infoSpy = jest.spyOn(core, 'info');
process.env.ACTIONS_RUNTIME_TOKEN = fs.readFileSync(path.join(__dirname, 'fixtures', 'runtimeToken.txt')).toString().trim();
await GitHub.printActionsRuntimeTokenACs();
expect(infoSpy).toHaveBeenCalledTimes(1);
expect(infoSpy).toHaveBeenCalledWith(`refs/heads/master: read/write`);
});
});

View File

@@ -36,7 +36,7 @@ export class Inputs {
return path.join(this.context.tmpDir(), 'metadata-file').split(path.sep).join(path.posix.sep);
}
public getBuildImageID(): string | undefined {
public resolveBuildImageID(): string | undefined {
const iidFile = this.getBuildImageIDFilePath();
if (!fs.existsSync(iidFile)) {
return undefined;
@@ -44,7 +44,7 @@ export class Inputs {
return fs.readFileSync(iidFile, {encoding: 'utf-8'}).trim();
}
public getBuildMetadata(): string | undefined {
public resolveBuildMetadata(): string | undefined {
const metadataFile = this.getBuildMetadataFilePath();
if (!fs.existsSync(metadataFile)) {
return undefined;
@@ -56,8 +56,8 @@ export class Inputs {
return content;
}
public getDigest(): string | undefined {
const metadata = this.getBuildMetadata();
public resolveDigest(): string | undefined {
const metadata = this.resolveBuildMetadata();
if (metadata === undefined) {
return undefined;
}
@@ -68,15 +68,15 @@ export class Inputs {
return undefined;
}
public generateBuildSecretString(kvp: string): string {
return this.generateBuildSecret(kvp, false);
public resolveBuildSecretString(kvp: string): string {
return this.resolveBuildSecret(kvp, false);
}
public generateBuildSecretFile(kvp: string): string {
return this.generateBuildSecret(kvp, true);
public resolveBuildSecretFile(kvp: string): string {
return this.resolveBuildSecret(kvp, true);
}
public generateBuildSecret(kvp: string, file: boolean): string {
public resolveBuildSecret(kvp: string, file: boolean): string {
const delimiterIndex = kvp.indexOf('=');
const key = kvp.substring(0, delimiterIndex);
let value = kvp.substring(delimiterIndex + 1);
@@ -105,11 +105,11 @@ export class Inputs {
return core.getBooleanInput(name) ? `builder-id=${builderID}` : 'false';
} catch (err) {
// not a valid boolean, so we assume it's a string
return this.getProvenanceAttrs(input);
return this.resolveProvenanceAttrs(input);
}
}
public getProvenanceAttrs(input: string): string {
public resolveProvenanceAttrs(input: string): string {
if (!input) {
return `builder-id=${this.context.provenanceBuilderID}`;
}

View File

@@ -15,11 +15,12 @@
*/
import {GitHub as Octokit} from '@actions/github/lib/utils';
import * as core from '@actions/core';
import * as github from '@actions/github';
import {Context} from '@actions/github/lib/context';
import jwt_decode from 'jwt-decode';
import {GitHubActionsRuntimeToken, GitHubRepo} from './types/github';
import {GitHubActionsRuntimeToken, GitHubActionsRuntimeTokenAC, GitHubRepo} from './types/github';
export interface GitHubOpts {
token?: string;
@@ -48,8 +49,43 @@ export class GitHub {
return process.env.GITHUB_API_URL || 'https://api.github.com';
}
static get actionsRuntimeToken(): GitHubActionsRuntimeToken {
static get actionsRuntimeToken(): GitHubActionsRuntimeToken | undefined {
const token = process.env['ACTIONS_RUNTIME_TOKEN'] || '';
return token ? jwt_decode<GitHubActionsRuntimeToken>(token) : {};
return token ? jwt_decode<GitHubActionsRuntimeToken>(token) : undefined;
}
public static async printActionsRuntimeTokenACs() {
let jwt: GitHubActionsRuntimeToken | undefined;
try {
jwt = GitHub.actionsRuntimeToken;
} catch (e) {
core.warning(`Cannot parse Actions Runtime Token: ${e.message}`);
return;
}
if (!jwt) {
core.warning(`ACTIONS_RUNTIME_TOKEN not set`);
return;
}
try {
<Array<GitHubActionsRuntimeTokenAC>>JSON.parse(`${jwt.ac}`).forEach(ac => {
let permission: string;
switch (ac.Permission) {
case 1:
permission = 'read';
break;
case 2:
permission = 'write';
break;
case 3:
permission = 'read/write';
break;
default:
permission = `unimplemented (${ac.Permission})`;
}
core.info(`${ac.Scope}: ${permission}`);
});
} catch (e) {
core.warning(`Cannot parse Actions Runtime Token Access Controls: ${e.message}`);
}
}
}

View File

@@ -29,3 +29,8 @@ export type GitHubRepo = OctoOpenApiTypes['schemas']['repository'];
export interface GitHubActionsRuntimeToken extends JwtPayload {
ac?: string;
}
export interface GitHubActionsRuntimeTokenAC {
Scope: string;
Permission: number;
}