Skip to content

Commit 9d2461e

Browse files
authored
workflows: Add a job for auditing release assets (#92829)
This checks to ensure that uploads are only made by 'approved' uploaders, which is just everyone who has uploaded a release asset in the past. We could do more, but this is just a simple implementation so we can put something in place and see how it works. For more discussion see: https://discourse.llvm.org/t/rfc-improve-binary-security/78121
1 parent 0413328 commit 9d2461e

File tree

2 files changed

+105
-0
lines changed

2 files changed

+105
-0
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import github
2+
import sys
3+
4+
def main():
5+
token = sys.argv[1]
6+
7+
gh = github.Github(login_or_token=token)
8+
repo = gh.get_repo("llvm/llvm-project")
9+
10+
uploaders = set(
11+
[
12+
"DimitryAndric",
13+
"stefanp-ibm",
14+
"lei137",
15+
"omjavaid",
16+
"nicolerabjohn",
17+
"amy-kwan",
18+
"mandlebug",
19+
"zmodem",
20+
"androm3da",
21+
"tru",
22+
"rovka",
23+
"rorth",
24+
"quinnlp",
25+
"kamaub",
26+
"abrisco",
27+
"jakeegan",
28+
"maryammo",
29+
"tstellar",
30+
"github-actions[bot]",
31+
]
32+
)
33+
34+
for release in repo.get_releases():
35+
print("Release:", release.title)
36+
for asset in release.get_assets():
37+
created_at = asset.created_at
38+
updated_at = (
39+
"" if asset.created_at == asset.updated_at else asset.updated_at
40+
)
41+
print(
42+
f"{asset.name} : {asset.uploader.login} [{created_at} {updated_at}] ( {asset.download_count} )"
43+
)
44+
if asset.uploader.login not in uploaders:
45+
with open('comment', 'w') as file:
46+
file.write(f'@{asset.uploader.login} is not a valid uploader.')
47+
sys.exit(1)
48+
49+
50+
if __name__ == "__main__":
51+
main()
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Release Asset Audit
2+
3+
on:
4+
workflow_dispatch:
5+
release:
6+
schedule:
7+
# * is a special character in YAML so you have to quote this string
8+
# Run once an hour
9+
- cron: '5 * * * *'
10+
11+
pull_request:
12+
paths:
13+
- ".github/workflows/release-asset-audit.py"
14+
- ".github/workflows/release-asset-audit.yml"
15+
16+
permissions:
17+
contents: read # Default everything to read-only
18+
19+
jobs:
20+
audit:
21+
name: "Release Asset Audit"
22+
runs-on: ubuntu-22.04
23+
if: github.repository == 'llvm/llvm-project'
24+
steps:
25+
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 #v4.1.6
26+
- name: "Run Audit Script"
27+
env:
28+
GITHUB_TOKEN: ${{ github.token }}
29+
run: |
30+
pip install --require-hashes -r ./llvm/utils/git/requirements.txt
31+
python3 ./.github/workflows/release-asset-audit.py $GITHUB_TOKEN
32+
- name: "File Issue"
33+
if: >-
34+
github.event_name != 'pull_request' &&
35+
failure()
36+
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7.0.1
37+
with:
38+
github-token: ${{ secrets.ISSUE_SUBSCRIBER_TOKEN }}
39+
script: |
40+
var fs = require('fs');
41+
var body = ''
42+
if (fs.existsSync('./comment')) {
43+
body = JSON.parse(fs.readFileSync('./comment')) + "\n\n";
44+
}
45+
body = body + `\n\nhttps://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`
46+
47+
const issue = await github.rest.issues.create({
48+
owner: context.repo.owner,
49+
repo: context.repo.repo,
50+
title: "Release Asset Audit Failed",
51+
labels: ['infrastructure'],
52+
body: body
53+
});
54+
console.log(issue);

0 commit comments

Comments
 (0)