Skip to content

Commit b8e2aeb

Browse files
Antony BaileyAntony Bailey
authored andcommitted
attempt to improve security
1 parent 5400c91 commit b8e2aeb

File tree

1 file changed

+123
-0
lines changed

1 file changed

+123
-0
lines changed

.github/workflows/sha-pins.yml

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
name: Check and update workflow action references
2+
3+
on:
4+
schedule:
5+
- cron: '0 0 * * 1' # Run weekly on Mondays
6+
workflow_dispatch: # Allow manual trigger
7+
8+
jobs:
9+
check-and-update-workflows:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: write
13+
pull-requests: write
14+
15+
steps:
16+
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
17+
18+
- name: Set up Python
19+
uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
20+
with:
21+
python-version: '3.11'
22+
23+
- name: Install dependencies
24+
run: |
25+
python -m pip install --upgrade pip
26+
pip install PyGithub pyyaml
27+
28+
- name: Check workflows and update action references
29+
env:
30+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
31+
run: |
32+
python - <<'EOF'
33+
import os
34+
import re
35+
import yaml
36+
import requests
37+
from github import Github
38+
39+
# Initialize GitHub client
40+
g = Github(os.environ["GITHUB_TOKEN"])
41+
repo = g.get_repo(os.environ["GITHUB_REPOSITORY"])
42+
43+
def get_latest_sha(action_owner, action_name, version):
44+
# Get the latest SHA for the given action and version
45+
url = f"https://api.github.com/repos/{action_owner}/{action_name}/commits/{version}"
46+
headers = {"Accept": "application/vnd.github.v3+json"}
47+
if "GITHUB_TOKEN" in os.environ:
48+
headers["Authorization"] = f"token {os.environ['GITHUB_TOKEN']}"
49+
50+
response = requests.get(url, headers=headers)
51+
if response.status_code == 200:
52+
return response.json()["sha"]
53+
return None
54+
55+
# Get all workflow files
56+
workflow_dir = ".github/workflows"
57+
updates = {}
58+
59+
for filename in os.listdir(workflow_dir):
60+
if filename.endswith((".yml", ".yaml")) and filename != "sha.yml":
61+
filepath = os.path.join(workflow_dir, filename)
62+
with open(filepath, 'r') as f:
63+
content = f.read()
64+
65+
# Look for uses: statements that don't include SHA
66+
lines = content.split('\n')
67+
updated_lines = lines.copy()
68+
needs_update = False
69+
70+
for i, line in enumerate(lines):
71+
# Match pattern like: uses: actions/checkout@v3 or uses: owner/repo@v1.2.3
72+
matches = re.search(r'^\s*uses:\s+([a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+)@(v\d+[^\s#]*)(?:\s+#.*)?$', line)
73+
if matches:
74+
action = matches.group(1)
75+
version = matches.group(2)
76+
# Skip if line already has SHA comment
77+
if not re.search(r'#\s*[a-f0-9]{40}', line):
78+
sha = get_latest_sha(action.split('/')[0], action.split('/')[1], version)
79+
if sha:
80+
# Update line with SHA as comment
81+
updated_line = re.sub(r'@(v\d+[^\s#]*)(?:\s+#.*)?$', f'@{sha[:40]} # {version}', line)
82+
updated_lines[i] = updated_line
83+
needs_update = True
84+
print(f"Updating {filename}: {line.strip()} -> {updated_line.strip()}")
85+
86+
if needs_update:
87+
updates[filepath] = '\n'.join(updated_lines)
88+
89+
if updates:
90+
branch_name = f"action-sha-updates-{os.environ.get('GITHUB_RUN_ID', '')}"
91+
default_branch = repo.default_branch
92+
93+
# Create a new branch
94+
ref = f"refs/heads/{branch_name}"
95+
try:
96+
repo.create_git_ref(ref=ref, sha=repo.get_branch(default_branch).commit.sha)
97+
except Exception as e:
98+
print(f"Error creating branch: {e}")
99+
exit(1)
100+
101+
# Update files
102+
commit_message = "chore: update GitHub Action references with SHA pins"
103+
for filepath, content in updates.items():
104+
try:
105+
file = repo.get_contents(filepath, ref=branch_name)
106+
repo.update_file(filepath, commit_message, content, file.sha, branch=branch_name)
107+
except Exception as e:
108+
print(f"Error updating {filepath}: {e}")
109+
110+
# Create PR
111+
try:
112+
pr = repo.create_pull(
113+
title="Update GitHub Action references with SHA pins",
114+
body="This PR updates GitHub Action references to include SHA pins for better security and reproducibility.",
115+
head=branch_name,
116+
base=default_branch
117+
)
118+
print(f"Created PR #{pr.number}: {pr.html_url}")
119+
except Exception as e:
120+
print(f"Error creating PR: {e}")
121+
else:
122+
print("No updates needed - all action references already use SHA pins.")
123+
EOF

0 commit comments

Comments
 (0)