From b8d1eb688475a710a3f5ace7c9af18633941e351 Mon Sep 17 00:00:00 2001 From: BJ Hargrave Date: Thu, 2 May 2024 16:41:11 -0400 Subject: [PATCH] Harden GitHub action workflows We use SHAs instead of tag names to refer to action versions. Dependabot will help use manage the SHAs. Update permissions to minimum necessary. Add harden-runner to monitor egress of action. After some time, we can tighten the egress to limit hosts/ports. We also update the maintainers script to generate markdown which passes markdownlint checking. Signed-off-by: BJ Hargrave --- .github/dco.yml | 4 -- .github/dependabot.yml | 17 +++++ .github/workflows/actionlint.yml | 34 +++++++--- .github/workflows/matchers/actionlint.json | 17 +++++ .../workflows/periodic-maintainers-update.yml | 64 +++++++++++++------ tools/maintainers/maintainers.py | 28 ++++---- tools/maintainers/requirements.txt | 1 - 7 files changed, 119 insertions(+), 46 deletions(-) delete mode 100644 .github/dco.yml create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/matchers/actionlint.json diff --git a/.github/dco.yml b/.github/dco.yml deleted file mode 100644 index de6cd3b..0000000 --- a/.github/dco.yml +++ /dev/null @@ -1,4 +0,0 @@ -# This enables DCO bot for you, please take a look https://github.com/probot/dco -# for more details. -require: - members: false diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..861bbc5 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: Apache-2.0 + +# GitHub Dependabot configuration file +version: 2 +updates: + + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + + # Maintain dependencies for Python scripts + - package-ecosystem: "pip" + directory: "/tools/maintainers" + schedule: + interval: "daily" diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml index 55b6377..9547da6 100644 --- a/.github/workflows/actionlint.yml +++ b/.github/workflows/actionlint.yml @@ -6,25 +6,43 @@ on: branches: - "main" paths: - - '.github/**' + - '.github/workflows/*.ya?ml' pull_request: branches: - "main" paths: - - '.github/**' + - '.github/workflows/*.ya?ml' + +env: + LC_ALL: en_US.UTF-8 defaults: run: shell: bash +permissions: + contents: read + jobs: actionlint: runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Download actionlint + - name: "Harden Runner" + uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - name: "Checkout" + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + with: + fetch-depth: 0 + + - name: "Download actionlint" id: get_actionlint - run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) 1.6.27 - - name: Check workflow files - run: PATH=".:$PATH" actionlint -color + run: | + bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/2d26fef7e97b8ab345791f5ade3252da47d083e3/scripts/download-actionlint.bash) + + - name: "Check workflow files" + run: | + echo "::add-matcher::.github/workflows/matchers/actionlint.json" + ${{ steps.get_actionlint.outputs.executable }} -color diff --git a/.github/workflows/matchers/actionlint.json b/.github/workflows/matchers/actionlint.json new file mode 100644 index 0000000..4613e16 --- /dev/null +++ b/.github/workflows/matchers/actionlint.json @@ -0,0 +1,17 @@ +{ + "problemMatcher": [ + { + "owner": "actionlint", + "pattern": [ + { + "regexp": "^(?:\\x1b\\[\\d+m)?(.+?)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*: (?:\\x1b\\[\\d+m)*(.+?)(?:\\x1b\\[\\d+m)* \\[(.+?)\\]$", + "file": 1, + "line": 2, + "column": 3, + "message": 4, + "code": 5 + } + ] + } + ] +} diff --git a/.github/workflows/periodic-maintainers-update.yml b/.github/workflows/periodic-maintainers-update.yml index ef0bdae..778beec 100644 --- a/.github/workflows/periodic-maintainers-update.yml +++ b/.github/workflows/periodic-maintainers-update.yml @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 + name: Periodic update of MAINTAINERS.md on: @@ -5,6 +7,13 @@ on: - cron: '0 0 * * *' # Runs every day at 00:00 UTC workflow_dispatch: # Allow manual trigger +env: + LC_ALL: en_US.UTF-8 + +defaults: + run: + shell: bash + jobs: update-maintainers: runs-on: ubuntu-latest @@ -12,32 +21,47 @@ jobs: contents: write pull-requests: write steps: - - name: Checkout repo - uses: actions/checkout@v2 + - name: "Harden Runner" + uses: step-security/harden-runner@a4aa98b93cab29d9b1101a6143fb8bce00e2eac4 # v2.7.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - name: Run script - run: ./tools/maintainers/maintainers.py tools/maintainers/teams.yaml > MAINTAINERS.md + - name: "Checkout" + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + with: + fetch-depth: 0 + + - name: "Setup Python" + uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 + with: + python-version: "3.11" + + - name: "Install Python Packages" + run: | + pip install -r tools/maintainers/requirements.txt + + - name: Update maintainers page + run: | + tools/maintainers/maintainers.py tools/maintainers/teams.yaml > MAINTAINERS.md env: GH_TOKEN: ${{ secrets.ORG_ACCESS_TOKEN }} - name: Check for changes id: git-diff - run: echo "CHANGED=$(if git diff --quiet --exit-code; then echo "false"; else echo "true"; fi)" >> "$GITHUB_ENV" - - - name: Commit and push if changed - if: env.CHANGED == 'true' run: | - git config --global user.name 'Maintainers Update Bot' - git config --global user.email 'ci@instructlab.io' - git commit -a -s -m "MAINTAINERS.md: automated update" - git push -f origin HEAD:maintainers-update + echo "changed=$(git diff --quiet --exit-code -- MAINTAINERS.md; echo "$?")" >> "$GITHUB_OUTPUT" - name: Create Pull Request - if: env.CHANGED == 'true' - uses: peter-evans/create-pull-request@v3 - with: - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: "Commit message" - title: "MAINTAINERS.md: automated update" - body: "Automated update of MAINTAINERS.md" - branch: "maintainers-update" \ No newline at end of file + if: steps.git-diff.outputs.changed == '1' + run: | + git config --global user.name 'Maintainers Update Bot' + git config --global user.email 'ci@instructlab.ai' + git checkout -b maintainers-update + git add -- MAINTAINERS.md + git commit -s -m "MAINTAINERS.md: automated update" + git push -u -f origin maintainers-update + if ! (gh pr list --head maintainers-update --json number | grep -q "number") ; then + gh pr create --title "MAINTAINERS.md: automated update" --body "Automated update of MAINTAINERS.md" + fi + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/tools/maintainers/maintainers.py b/tools/maintainers/maintainers.py index 622a891..46f1b5e 100755 --- a/tools/maintainers/maintainers.py +++ b/tools/maintainers/maintainers.py @@ -10,11 +10,12 @@ def get_team_members(team_slug): - org = 'instructlab' - result = subprocess.run(['gh', 'api', '--method', 'GET', - f'/orgs/{org}/teams/{team_slug}/members'], - stdout=subprocess.PIPE) - output = result.stdout.decode('utf-8') + org = "instructlab" + output = subprocess.check_output( + ["gh", "api", "--method", "GET", f"/orgs/{org}/teams/{team_slug}/members"], + text=True, + encoding="utf-8", + ) return json.loads(output) @@ -28,23 +29,24 @@ def main(argv=None): teams_yaml = argv[1] - with open(teams_yaml, 'r') as f: + with open(teams_yaml, "r", encoding="utf-8") as f: teams = yaml.load(f, Loader=yaml.FullLoader) print("# Maintainers\n") - print("*To update see [tools/maintainers/README.md](tools/maintainers/README.md)*\n") + print( + "*To update see [tools/maintainers/README.md](tools/maintainers/README.md)*" + ) for section, teams in teams.items(): - print("## %s\n" % section) + print("\n## %s" % section) for t in teams: - print("### %s\n" % t["name"]) + print("\n### %s\n" % t["name"]) print("%s\n" % t["desc"]) members = get_team_members(t["slug"]) - members_sorted = sorted(members, key=lambda d: d['login'].lower()) + members_sorted = sorted(members, key=lambda d: d["login"].lower()) for m in members_sorted: print("- [%s](https://github.com/%s)" % (m["login"], m["login"])) - print("\n") return 0 -if __name__ == '__main__': - sys.exit(main()) \ No newline at end of file +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/maintainers/requirements.txt b/tools/maintainers/requirements.txt index 376fe9d..94b1c7e 100644 --- a/tools/maintainers/requirements.txt +++ b/tools/maintainers/requirements.txt @@ -1,4 +1,3 @@ # SPDX-License-Identifier: Apache-2.0 PyYAML>=6.0.1,<7.0.0 -requests>=2.31.0,<3.0.0