Merge pull request #23 from crazy-max/github-print-art
github: printActionsRuntimeTokenACs
This commit is contained in:
@@ -50,7 +50,7 @@ afterEach(() => {
|
|||||||
rimraf.sync(tmpDir);
|
rimraf.sync(tmpDir);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getBuildImageID', () => {
|
describe('resolveBuildImageID', () => {
|
||||||
it('matches', async () => {
|
it('matches', async () => {
|
||||||
const buildx = new Buildx({
|
const buildx = new Buildx({
|
||||||
context: new Context()
|
context: new Context()
|
||||||
@@ -58,31 +58,31 @@ describe('getBuildImageID', () => {
|
|||||||
const imageID = 'sha256:bfb45ab72e46908183546477a08f8867fc40cebadd00af54b071b097aed127a9';
|
const imageID = 'sha256:bfb45ab72e46908183546477a08f8867fc40cebadd00af54b071b097aed127a9';
|
||||||
const imageIDFile = buildx.inputs.getBuildImageIDFilePath();
|
const imageIDFile = buildx.inputs.getBuildImageIDFilePath();
|
||||||
await fs.writeFileSync(imageIDFile, imageID);
|
await fs.writeFileSync(imageIDFile, imageID);
|
||||||
const expected = buildx.inputs.getBuildImageID();
|
const expected = buildx.inputs.resolveBuildImageID();
|
||||||
expect(expected).toEqual(imageID);
|
expect(expected).toEqual(imageID);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getBuildMetadata', () => {
|
describe('resolveBuildMetadata', () => {
|
||||||
it('matches', async () => {
|
it('matches', async () => {
|
||||||
const buildx = new Buildx({
|
const buildx = new Buildx({
|
||||||
context: new Context()
|
context: new Context()
|
||||||
});
|
});
|
||||||
const metadataFile = buildx.inputs.getBuildMetadataFilePath();
|
const metadataFile = buildx.inputs.getBuildMetadataFilePath();
|
||||||
await fs.writeFileSync(metadataFile, metadata);
|
await fs.writeFileSync(metadataFile, metadata);
|
||||||
const expected = buildx.inputs.getBuildMetadata();
|
const expected = buildx.inputs.resolveBuildMetadata();
|
||||||
expect(expected).toEqual(metadata);
|
expect(expected).toEqual(metadata);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getDigest', () => {
|
describe('resolveDigest', () => {
|
||||||
it('matches', async () => {
|
it('matches', async () => {
|
||||||
const buildx = new Buildx({
|
const buildx = new Buildx({
|
||||||
context: new Context()
|
context: new Context()
|
||||||
});
|
});
|
||||||
const metadataFile = buildx.inputs.getBuildMetadataFilePath();
|
const metadataFile = buildx.inputs.getBuildMetadataFilePath();
|
||||||
await fs.writeFileSync(metadataFile, metadata);
|
await fs.writeFileSync(metadataFile, metadata);
|
||||||
const expected = buildx.inputs.getDigest();
|
const expected = buildx.inputs.resolveDigest();
|
||||||
expect(expected).toEqual('sha256:b09b9482c72371486bb2c1d2c2a2633ed1d0b8389e12c8d52b9e052725c0c83c');
|
expect(expected).toEqual('sha256:b09b9482c72371486bb2c1d2c2a2633ed1d0b8389e12c8d52b9e052725c0c83c');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -136,7 +136,7 @@ describe('getProvenanceInput', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getProvenanceAttrs', () => {
|
describe('resolveProvenanceAttrs', () => {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
test.each([
|
test.each([
|
||||||
[
|
[
|
||||||
@@ -163,11 +163,11 @@ describe('getProvenanceAttrs', () => {
|
|||||||
const buildx = new Buildx({
|
const buildx = new Buildx({
|
||||||
context: new Context()
|
context: new Context()
|
||||||
});
|
});
|
||||||
expect(buildx.inputs.getProvenanceAttrs(input)).toEqual(expected);
|
expect(buildx.inputs.resolveProvenanceAttrs(input)).toEqual(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('generateBuildSecret', () => {
|
describe('resolveBuildSecret', () => {
|
||||||
test.each([
|
test.each([
|
||||||
['A_SECRET=abcdef0123456789', false, 'A_SECRET', 'abcdef0123456789', null],
|
['A_SECRET=abcdef0123456789', false, 'A_SECRET', 'abcdef0123456789', null],
|
||||||
['GIT_AUTH_TOKEN=abcdefghijklmno=0123456789', false, 'GIT_AUTH_TOKEN', 'abcdefghijklmno=0123456789', null],
|
['GIT_AUTH_TOKEN=abcdefghijklmno=0123456789', false, 'GIT_AUTH_TOKEN', 'abcdefghijklmno=0123456789', null],
|
||||||
@@ -184,9 +184,9 @@ describe('generateBuildSecret', () => {
|
|||||||
});
|
});
|
||||||
let secret: string;
|
let secret: string;
|
||||||
if (file) {
|
if (file) {
|
||||||
secret = buildx.inputs.generateBuildSecretFile(kvp);
|
secret = buildx.inputs.resolveBuildSecretFile(kvp);
|
||||||
} else {
|
} else {
|
||||||
secret = buildx.inputs.generateBuildSecretString(kvp);
|
secret = buildx.inputs.resolveBuildSecretString(kvp);
|
||||||
}
|
}
|
||||||
expect(secret).toEqual(`id=${exKey},src=${tmpName}`);
|
expect(secret).toEqual(`id=${exKey},src=${tmpName}`);
|
||||||
expect(fs.readFileSync(tmpName, 'utf-8')).toEqual(exValue);
|
expect(fs.readFileSync(tmpName, 'utf-8')).toEqual(exValue);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
import {describe, expect, jest, it, beforeEach, afterEach} from '@jest/globals';
|
import {describe, expect, jest, it, beforeEach, afterEach} from '@jest/globals';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
|
||||||
import {GitHub} from '../src/github';
|
import {GitHub} from '../src/github';
|
||||||
import {GitHubRepo} from '../src/types/github';
|
import {GitHubRepo} from '../src/types/github';
|
||||||
@@ -101,12 +102,52 @@ describe('actionsRuntimeToken', () => {
|
|||||||
});
|
});
|
||||||
it('empty', async () => {
|
it('empty', async () => {
|
||||||
process.env.ACTIONS_RUNTIME_TOKEN = '';
|
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 () => {
|
it('fixture', async () => {
|
||||||
process.env.ACTIONS_RUNTIME_TOKEN = fs.readFileSync(path.join(__dirname, 'fixtures', 'runtimeToken.txt')).toString().trim();
|
process.env.ACTIONS_RUNTIME_TOKEN = fs.readFileSync(path.join(__dirname, 'fixtures', 'runtimeToken.txt')).toString().trim();
|
||||||
const runtimeToken = GitHub.actionsRuntimeToken;
|
const runtimeToken = GitHub.actionsRuntimeToken;
|
||||||
expect(runtimeToken.ac).toEqual('[{"Scope":"refs/heads/master","Permission":3}]');
|
expect(runtimeToken?.ac).toEqual('[{"Scope":"refs/heads/master","Permission":3}]');
|
||||||
expect(runtimeToken.iss).toEqual('vstoken.actions.githubusercontent.com');
|
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`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export class Inputs {
|
|||||||
return path.join(this.context.tmpDir(), 'metadata-file').split(path.sep).join(path.posix.sep);
|
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();
|
const iidFile = this.getBuildImageIDFilePath();
|
||||||
if (!fs.existsSync(iidFile)) {
|
if (!fs.existsSync(iidFile)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -44,7 +44,7 @@ export class Inputs {
|
|||||||
return fs.readFileSync(iidFile, {encoding: 'utf-8'}).trim();
|
return fs.readFileSync(iidFile, {encoding: 'utf-8'}).trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
public getBuildMetadata(): string | undefined {
|
public resolveBuildMetadata(): string | undefined {
|
||||||
const metadataFile = this.getBuildMetadataFilePath();
|
const metadataFile = this.getBuildMetadataFilePath();
|
||||||
if (!fs.existsSync(metadataFile)) {
|
if (!fs.existsSync(metadataFile)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -56,8 +56,8 @@ export class Inputs {
|
|||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDigest(): string | undefined {
|
public resolveDigest(): string | undefined {
|
||||||
const metadata = this.getBuildMetadata();
|
const metadata = this.resolveBuildMetadata();
|
||||||
if (metadata === undefined) {
|
if (metadata === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -68,15 +68,15 @@ export class Inputs {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public generateBuildSecretString(kvp: string): string {
|
public resolveBuildSecretString(kvp: string): string {
|
||||||
return this.generateBuildSecret(kvp, false);
|
return this.resolveBuildSecret(kvp, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public generateBuildSecretFile(kvp: string): string {
|
public resolveBuildSecretFile(kvp: string): string {
|
||||||
return this.generateBuildSecret(kvp, true);
|
return this.resolveBuildSecret(kvp, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public generateBuildSecret(kvp: string, file: boolean): string {
|
public resolveBuildSecret(kvp: string, file: boolean): string {
|
||||||
const delimiterIndex = kvp.indexOf('=');
|
const delimiterIndex = kvp.indexOf('=');
|
||||||
const key = kvp.substring(0, delimiterIndex);
|
const key = kvp.substring(0, delimiterIndex);
|
||||||
let value = kvp.substring(delimiterIndex + 1);
|
let value = kvp.substring(delimiterIndex + 1);
|
||||||
@@ -105,11 +105,11 @@ export class Inputs {
|
|||||||
return core.getBooleanInput(name) ? `builder-id=${builderID}` : 'false';
|
return core.getBooleanInput(name) ? `builder-id=${builderID}` : 'false';
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// not a valid boolean, so we assume it's a string
|
// 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) {
|
if (!input) {
|
||||||
return `builder-id=${this.context.provenanceBuilderID}`;
|
return `builder-id=${this.context.provenanceBuilderID}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,11 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {GitHub as Octokit} from '@actions/github/lib/utils';
|
import {GitHub as Octokit} from '@actions/github/lib/utils';
|
||||||
|
import * as core from '@actions/core';
|
||||||
import * as github from '@actions/github';
|
import * as github from '@actions/github';
|
||||||
import {Context} from '@actions/github/lib/context';
|
import {Context} from '@actions/github/lib/context';
|
||||||
import jwt_decode from 'jwt-decode';
|
import jwt_decode from 'jwt-decode';
|
||||||
|
|
||||||
import {GitHubActionsRuntimeToken, GitHubRepo} from './types/github';
|
import {GitHubActionsRuntimeToken, GitHubActionsRuntimeTokenAC, GitHubRepo} from './types/github';
|
||||||
|
|
||||||
export interface GitHubOpts {
|
export interface GitHubOpts {
|
||||||
token?: string;
|
token?: string;
|
||||||
@@ -48,8 +49,43 @@ export class GitHub {
|
|||||||
return process.env.GITHUB_API_URL || 'https://api.github.com';
|
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'] || '';
|
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}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,3 +29,8 @@ export type GitHubRepo = OctoOpenApiTypes['schemas']['repository'];
|
|||||||
export interface GitHubActionsRuntimeToken extends JwtPayload {
|
export interface GitHubActionsRuntimeToken extends JwtPayload {
|
||||||
ac?: string;
|
ac?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GitHubActionsRuntimeTokenAC {
|
||||||
|
Scope: string;
|
||||||
|
Permission: number;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user