ci: harden privileged workflow trust boundaries
This commit is contained in:
Executable
+61
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from pathlib import Path
|
||||
import unittest
|
||||
|
||||
from check_workflow_security import check_workflow
|
||||
|
||||
|
||||
SAFE_SHA = "a" * 40
|
||||
|
||||
|
||||
class WorkflowSecurityTests(unittest.TestCase):
|
||||
def check(self, text: str) -> list[str]:
|
||||
return check_workflow(Path(".github/workflows/test.yml"), text)
|
||||
|
||||
def test_accepts_read_only_pull_request_workflow_with_pinned_action(self) -> None:
|
||||
errors = self.check(
|
||||
f"""name: Test
|
||||
on:
|
||||
pull_request:
|
||||
jobs:
|
||||
test:
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@{SAFE_SHA} # v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
"""
|
||||
)
|
||||
self.assertEqual(errors, [])
|
||||
|
||||
def test_rejects_mutable_action_reference(self) -> None:
|
||||
errors = self.check("jobs:\n test:\n steps:\n - uses: actions/checkout@v7\n")
|
||||
self.assertTrue(any("full commit SHA" in error for error in errors))
|
||||
|
||||
def test_rejects_secrets_in_pull_request_workflow(self) -> None:
|
||||
errors = self.check(
|
||||
"on:\n pull_request:\njobs:\n test:\n env:\n TOKEN: ${{ secrets.TOKEN }}\n"
|
||||
)
|
||||
self.assertTrue(any("must not reference repository secrets" in error for error in errors))
|
||||
|
||||
def test_rejects_write_permission_in_pull_request_workflow(self) -> None:
|
||||
errors = self.check(
|
||||
"on:\n pull_request:\njobs:\n test:\n permissions:\n contents: write\n"
|
||||
)
|
||||
self.assertTrue(any("must not request write permissions" in error for error in errors))
|
||||
|
||||
def test_rejects_privileged_untrusted_trigger(self) -> None:
|
||||
errors = self.check("on:\n pull_request_target:\n")
|
||||
self.assertTrue(any("unaudited privileged trigger" in error for error in errors))
|
||||
|
||||
def test_rejects_mutable_raw_github_download(self) -> None:
|
||||
errors = self.check(
|
||||
"jobs:\n test:\n steps:\n - run: curl https://raw.githubusercontent.com/o/r/main/tool.sh\n"
|
||||
)
|
||||
self.assertTrue(any("immutable commit" in error for error in errors))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user