-
-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathindex.js
108 lines (86 loc) · 3.03 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
const core = require('@actions/core');
const fs = require('fs');
const glob = require('@actions/glob');
const path = require('path');
const yaml = require('yaml');
const sha1 = /\b[a-f0-9]{40}\b/i;
const sha256 = /\b[A-Fa-f0-9]{64}\b/i;
async function run() {
try {
const allowlist = core.getInput('allowlist');
const isDryRun = core.getInput('dry_run') === 'true';
const workflowsPath = process.env['ZG_WORKFLOWS_PATH'] || '.github/workflows';
const globber = await glob.create([workflowsPath + '/*.yaml', workflowsPath + '/*.yml'].join('\n'));
let actionHasError = false;
for await (const file of globber.globGenerator()) {
const basename = path.basename(file);
const fileContents = fs.readFileSync(file, 'utf8');
const yamlContents = yaml.parse(fileContents);
const jobs = yamlContents['jobs'];
let fileHasError = false;
if (jobs === undefined) {
core.setFailed(`The "${basename}" workflow does not contain jobs.`);
}
core.startGroup(workflowsPath + '/' + basename);
for (const job in jobs) {
const uses = jobs[job]['uses'];
const steps = jobs[job]['steps'];
if (assertUsesVersion(uses)) {
if (!assertUsesSha(uses) && !assertUsesAllowlist(uses, allowlist)) {
actionHasError = true;
fileHasError = true;
reportError(`${uses} is not pinned to a full length commit SHA.`, isDryRun);
}
} else if (steps !== undefined) {
for (const step of steps) {
const uses = step['uses'];
if (assertUsesVersion(uses) && !assertUsesSha(uses) && !assertUsesAllowlist(uses, allowlist)) {
actionHasError = true;
fileHasError = true;
reportError(`${uses} is not pinned to a full length commit SHA.`, isDryRun);
}
}
} else {
core.warning(`The "${job}" job of the "${basename}" workflow does not contain uses or steps.`);
}
}
if (!fileHasError) {
core.info('No issues were found.')
}
core.endGroup();
}
if (!isDryRun && actionHasError) {
throw new Error('At least one workflow contains an unpinned GitHub Action version.');
}
} catch (error) {
core.setFailed(error.message);
}
}
run();
function assertUsesVersion(uses) {
return typeof uses === 'string' && uses.includes('@');
}
function assertUsesSha(uses) {
if (uses.startsWith('docker://')) {
return sha256.test(uses.substr(uses.indexOf('sha256:') + 7));
}
return sha1.test(uses.substr(uses.indexOf('@') + 1));
}
function assertUsesAllowlist(uses, allowlist) {
if (!allowlist) {
return false;
}
const action = uses.substr(0, uses.indexOf('@'));
const isAllowed = allowlist.split(/\r?\n/).some((allow) => action.startsWith(allow));
if(isAllowed) {
core.info(`${action} matched allowlist — ignoring action.`)
}
return isAllowed;
}
function reportError(message, isDryRun) {
if (isDryRun) {
core.warning(message);
} else {
core.error(message);
}
}