From 96e9ff5889f2a1b1bf0304e52073499c80d51e3d Mon Sep 17 00:00:00 2001 From: Matthew Donoughe Date: Wed, 10 Jul 2024 13:02:13 -0400 Subject: [PATCH] add action for pushing policies --- .github/workflows/{lint.yml => main.yml} | 15 ++++- .github/workflows/upload.py | 74 ++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 2 deletions(-) rename .github/workflows/{lint.yml => main.yml} (67%) create mode 100644 .github/workflows/upload.py diff --git a/.github/workflows/lint.yml b/.github/workflows/main.yml similarity index 67% rename from .github/workflows/lint.yml rename to .github/workflows/main.yml index 55c4192..fe25178 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/main.yml @@ -1,11 +1,10 @@ -name: Lint policies - on: push: workflow_dispatch: jobs: lint: + name: Lint policies runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -27,3 +26,15 @@ jobs: - name: Regal Lint if: ${{ !cancelled() }} run: regal lint --format github . + + deploy: + name: Deploy policies + runs-on: ubuntu-latest + needs: lint + if: success() && github.ref == 'refs/heads/main' + + steps: + - uses: actions/checkout@v4 + + - name: Upload + run: .github/workflows/upload.py diff --git a/.github/workflows/upload.py b/.github/workflows/upload.py new file mode 100644 index 0000000..4e052a9 --- /dev/null +++ b/.github/workflows/upload.py @@ -0,0 +1,74 @@ +""" +Script to upload a set of policy files to Phylum. +""" + +import io +import json +import os +from pathlib import Path +import shutil +from urllib.error import HTTPError +from urllib.parse import quote +from urllib.request import Request, urlopen +import uuid + +# Set the group name if you are uploading policies for a group. +GROUP_NAME = None +token = os.environ["PHYLUM_TOKEN"] +version = os.environ.get("GITHUB_SHA", None) +BASE_ADDRESS = "https://api.staging.phylum.io/api/" + +# Build a multipart form body. +boundary = uuid.uuid4().hex +body = io.BytesIO() + +for file in Path.cwd().glob("*.rego"): + body.write(f"--{boundary}\r\n".encode("ascii")) + body.write( + f'Content-Disposition: form-data; name="{file.stem}"; filename="{file.name}"\r\n'.encode( + "ascii" + ) + ) + body.write(b"Content-Type: text/plain\r\n\r\n") + + with file.open("rb") as f: + shutil.copyfileobj(f, body) + + body.write(b"\r\n") + +body.write(f"--{boundary}--".encode("ascii")) + +# Send the request. +if GROUP_NAME is None: + endpoint = f"{BASE_ADDRESS}v0/available-policies" +else: + endpoint = ( + f"{BASE_ADDRESS}v0/groups/{quote(GROUP_NAME, safe='')}/available-policies" + ) + +if version is not None: + endpoint = f"{endpoint}?version={quote(version, safe='')}" + +request = Request( + endpoint, + data=body.getbuffer(), + headers={ + "Authorization": f"Bearer {token}", + "Content-Type": f'multipart/form-data;boundary="{boundary}"', + }, + method="PUT", +) + +try: + with urlopen(request) as f: + pass +except HTTPError as error: + response = error.read().decode("utf8", errors="replace") + try: + # Phylum's API returns JSON error descriptions. + # If that's what we got, reformat it to be more readable. + response = json.dumps(json.loads(response), indent=2) + except json.JSONDecodeError: + pass + print(response) + exit(1)