Skip to content

Commit 4805fb6

Browse files
committed
Merge branch 'main' into unknown
2 parents 987209b + 1b82c26 commit 4805fb6

File tree

8 files changed

+9816
-12350
lines changed

8 files changed

+9816
-12350
lines changed

.github/workflows/test.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
os:
1515
- ubuntu-latest # eventually will pick up the next version and test it
1616
- ubuntu-24.04 # beta
17-
- ubuntu-22.04 # ubuntu-latest
17+
- ubuntu-22.04 # ubuntu-latest
1818
- ubuntu-20.04
1919
- macos-latest # eventually will pick up the next version and test it
2020
- macos-14 # macos-latest
@@ -25,9 +25,9 @@ jobs:
2525

2626
runs-on: ${{ matrix.os }}
2727
steps:
28-
- uses: GitHubSecurityLab/actions-permissions/monitor@main
28+
- uses: GitHubSecurityLab/actions-permissions/monitor@main
2929
- uses: actions/checkout@v4
30-
30+
3131
# test github-script CA
3232
- name: github-script
3333
uses: actions/github-script@v7
@@ -38,7 +38,7 @@ jobs:
3838
owner: context.repo.owner,
3939
repo: context.repo.repo,
4040
});
41-
41+
4242
# test gh CA
4343
- name: gh api
4444
env:

advisor/dist/index.js

Lines changed: 3023 additions & 1822 deletions
Large diffs are not rendered by default.

advisor/index.js

Lines changed: 94 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@ const core = require('@actions/core');
22
const github = require('@actions/github');
33
const AdmZip = require('adm-zip');
44

5+
let verbose = false;
6+
let log = null;
7+
let debug = (msg) => { if (verbose) { log(msg); } }
8+
59
async function analyze(name, count, token, owner, repo, branch) {
6-
console.log(`Analyzing ${name} for the last ${count} successful runs.\n`);
10+
log(`Analyzing ${name} for the last ${count} successful runs.\n`);
711
const octokit = github.getOctokit(token)
812

913
const runs = await octokit.rest.actions.listWorkflowRuns({
@@ -20,57 +24,55 @@ async function analyze(name, count, token, owner, repo, branch) {
2024
let wasUnknown = false;
2125

2226
for (const run of runs.data.workflow_runs) {
23-
if (process.env.RUNNER_DEBUG)
24-
console.log(`Analyzing run ${run.id}...`);
27+
debug(`Analyzing run ${run.id}...`);
2528

2629
const jobs = await octokit.rest.actions.listJobsForWorkflowRun({
2730
owner: owner,
2831
repo: repo,
2932
run_id: run.id,
3033
});
3134

32-
if (process.env.RUNNER_DEBUG)
33-
console.log(`Found ${jobs.data.jobs.length} jobs.`)
35+
debug(`Found ${jobs.data.jobs.length} jobs.`)
3436

3537
const artifacts = await octokit.rest.actions.listWorkflowRunArtifacts({
3638
owner: owner,
3739
repo: repo,
3840
run_id: run.id,
3941
});
4042

41-
if (process.env.RUNNER_DEBUG)
42-
console.log(`${artifacts.data.artifacts.length} artifacts...`)
43+
debug(`${artifacts.data.artifacts.length} artifacts...`)
4344

4445
for (const job of jobs.data.jobs) {
4546
if (job.conclusion !== 'success')
4647
continue;
4748

48-
if (process.env.RUNNER_DEBUG) {
49-
console.log(`${job.name} ${job.id} was successful...`);
50-
console.log(`Downloading logs for job id ${job.id}...`);
51-
}
49+
debug(`${job.name} ${job.id} was successful...`);
50+
debug(`Downloading logs for job id ${job.id}...`);
5251

53-
let log = null;
52+
let workflowRunLog = null;
5453
try {
55-
log = await octokit.rest.actions.downloadJobLogsForWorkflowRun({
54+
workflowRunLog = await octokit.rest.actions.downloadJobLogsForWorkflowRun({
5655
owner: owner,
5756
repo: repo,
5857
job_id: job.id,
5958
});
6059
} catch (e) {
61-
if (process.env.RUNNER_DEBUG)
62-
console.log(`Logs for the job ${job.id} are not available.`);
60+
debug(`Logs for the job ${job.id} are not available.`);
6361
continue;
6462
}
6563

66-
const logUploadMatch = log.data.match(/^.* Container for artifact \"(.*-permissions-[a-z0-9]+)\" successfully created\. Starting upload of file\(s\)$/m);
67-
if (!logUploadMatch)
64+
const logUploadMatch = workflowRunLog.data.match(/([^ "]+-permissions-[a-z0-9]{32})/m);
65+
if (!logUploadMatch) {
66+
debug(`Cannot find the magic string. Skipping.`);
6867
continue;
68+
}
6969
const artifactName = logUploadMatch[1];
70+
debug(`Looking for artifactName ${artifactName}`);
7071
const jobName = artifactName.split('-').slice(0, -2).join('-');
7172

7273
for (const artifact of artifacts.data.artifacts) {
7374
if (artifact.name === artifactName) {
75+
debug(`Downloading artifact id ${artifact.id}`);
7476
const download = await octokit.rest.actions.downloadArtifact({
7577
owner: owner,
7678
repo: repo,
@@ -110,62 +112,105 @@ async function analyze(name, count, token, owner, repo, branch) {
110112
return [permissions, wasUnknown];
111113
}
112114

113-
async function run(name, count, token, owner, repo, branch) {
115+
async function run(token, name, count, owner, repo, branch, format) {
114116
const [permissions, wasUnknown] = await analyze(name, count, token, owner, repo, branch);
115117

116118
let summary = core.summary.addHeading(`Minimal required permissions for ${name}:`);
117-
console.log(`Minimal required permissions for ${name}:`);
119+
log(`Minimal required permissions for ${name}:`);
118120

119121
if (wasUnknown) {
120122
summary.addRaw("\nAt least one call wasn't recognized. Some permissions are unknown. Check the workflow runs.\n");
121123
}
122124

123-
if (permissions.size === 0) {
124-
summary = summary.addRaw('No permissions logs were found.');
125-
console.log('No permissions logs were found.');
126-
} else {
127-
for (const [jobName, jobPermissions] of permissions) {
128-
summary = summary.addHeading(`${jobName}:`, 2);
129-
console.log(`---------------------= ${jobName} =---------------------`);
130-
131-
let codeBlock = '';
132-
if (jobPermissions.size === 0) {
133-
codeBlock += 'permissions: {}';
134-
console.log('permissions: {}');
135-
} else {
136-
codeBlock += 'permissions:\n';
137-
console.log('permissions:');
138-
for (const [kind, perm] of jobPermissions) {
139-
codeBlock += ` ${kind}: ${perm}\n`;
140-
console.log(` ${kind}: ${perm}`);
125+
try {
126+
if (permissions.size === 0) {
127+
summary = summary.addRaw('No permissions logs were found.');
128+
throw new Error('No permissions logs were found.');
129+
} else {
130+
let additionalIndent = '';
131+
if (format)
132+
additionalIndent = ' ';
133+
134+
for (const [jobName, jobPermissions] of permissions) {
135+
summary = summary.addHeading(`${jobName}:`, 2);
136+
log(`---------------------= ${jobName} =---------------------`);
137+
if (format)
138+
console.log(`${jobName}:`);
139+
140+
let codeBlock = '';
141+
if (jobPermissions.size === 0) {
142+
codeBlock += `${additionalIndent}permissions: {}`;
143+
} else {
144+
codeBlock += `${additionalIndent}permissions:\n`;
145+
for (const [kind, perm] of jobPermissions) {
146+
codeBlock += `${additionalIndent} ${kind}: ${perm}\n`;
147+
}
141148
}
149+
150+
console.log(codeBlock); // write always
151+
summary = summary.addCodeBlock(codeBlock, 'yaml');
142152
}
143-
144-
summary = summary.addCodeBlock(codeBlock, 'yaml');
145153
}
146-
}
147-
148-
if (process.env.GITHUB_ACTIONS) {
149-
await summary.write();
154+
} finally {
155+
if (process.env.GITHUB_ACTIONS) {
156+
await summary.write();
157+
}
150158
}
151159
}
152160

153-
if (!process.env.GITHUB_ACTIONS && process.argv.length !== 7) {
154-
console.log('Usage: node index.js <number_of_the_last_runs> <github_owner> <repo_name> <branch_name>');
155-
console.log('For example: node index.js ci.yml 10 github actions-permissions main');
161+
function printUsageAndExit() {
162+
console.log('Usage: node index.js <number_of_the_last_runs> <github_owner> <repo_name> <branch_name> [--format yaml] [--verbose]');
163+
console.log('For example: node index.js ci.yml 10 github actions-permissions main --format yaml --verbose');
156164
process.exit(1);
157165
}
158166

167+
verbose = false;
168+
log = console.log;
169+
159170
if (process.env.GITHUB_ACTIONS) {
160171
const name = core.getInput('name');
161172
const count = core.getInput('count');
162173
const token = core.getInput('token');
174+
verbose = process.env.RUNNER_DEBUG ? true : false;
175+
const branch = github.context.ref.split('/').slice(-1)[0];
176+
const format = null;
163177

164-
run(name, count, token, github.context.repo.owner, github.context.repo.repo, github.context.ref.split('/').slice(-1)[0]).catch(error => {
178+
run(token, name, count, github.context.repo.owner, github.context.repo.repo, branch, format).catch(error => {
165179
core.setFailed(error.message);
166180
});
167181
} else {
168-
run(process.argv[2], process.argv[3], process.env.GITHUB_TOKEN, process.argv[4], process.argv[5], process.argv[6]).catch(error => {
169-
console.log(`Error: ${error.message}`);
182+
const args = process.argv.slice(2);
183+
const outputIndex = args.indexOf('--format');
184+
let format = null;
185+
186+
if (outputIndex !== -1) {
187+
if (outputIndex + 1 >= args.length) {
188+
printUsageAndExit();
189+
}
190+
format = args[outputIndex + 1];
191+
if (!format || format !== 'yaml') {
192+
printUsageAndExit();
193+
}
194+
args.splice(outputIndex, 2); // Remove --output and its value from args
195+
}
196+
197+
const debugIndex = args.indexOf('--verbose');
198+
if (debugIndex !== -1) {
199+
verbose = true;
200+
args.splice(debugIndex, 1); // Remove --verbose from args
201+
}
202+
203+
if (args.length !== 5) {
204+
printUsageAndExit();
205+
}
206+
207+
const [name, count, owner, repo, branch] = args;
208+
if (format !== null) {
209+
log = () => {};
210+
}
211+
212+
run(process.env.GITHUB_TOKEN, name, count, owner, repo, branch, format).catch(error => {
213+
console.error(`Error: ${error.message}`);
214+
exit(2);
170215
});
171216
}

0 commit comments

Comments
 (0)