From de0c8587b20345a0c93df9e1b6317bc375e2d901 Mon Sep 17 00:00:00 2001 From: "prompt-driven-github[bot]" <257657681+prompt-driven-github[bot]@users.noreply.github.com> Date: Sat, 31 Jan 2026 22:53:31 +0000 Subject: [PATCH] chore: add PDD secrets dispatch workflow [automated] --- .github/workflows/pdd-secrets-dispatch.yml | 126 +++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 .github/workflows/pdd-secrets-dispatch.yml diff --git a/.github/workflows/pdd-secrets-dispatch.yml b/.github/workflows/pdd-secrets-dispatch.yml new file mode 100644 index 0000000..8041997 --- /dev/null +++ b/.github/workflows/pdd-secrets-dispatch.yml @@ -0,0 +1,126 @@ +name: PDD Secrets Dispatch + +on: + repository_dispatch: + types: [pdd-secrets] + +jobs: + provide-secrets: + runs-on: ubuntu-latest + steps: + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install Dependencies + run: pip install cryptography requests + + - name: Encrypt and Send Secrets + shell: python + env: + SECRETS_CONTEXT: ${{ toJSON(secrets) }} + PAYLOAD_CONTEXT: ${{ toJSON(github.event.client_payload) }} + WORKER_PUBLIC_KEY: | + -----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwLc2Vn2VErKtvPa27Iek + ePSZuWdagaiA9wfce4+4GR3aDNCsv24btyttNapgqO1pkB7mVATp4WJCD7F3CDMU + ddDsi07PReXJrdMp+/IC0eGblHPPpJyZOh/5ZoIo2oJPOHjGWwEQO75JgT8jtvUh + L2o7lYuFS1BwMQpdHifpxDWdn8yXo6SvV7k0UISEWKn5sa4gcalOhYiEjFUNt4pt + a7/HJQQu6K3AJKoUT8eTsftV5o1SHP0C8wB85hOehdzEM5uSpCb6aZ/cVqU38z15 + NjqEVabaUquswZwHQ1aHmK+CktX0KfBKa/DYk4ZqW1gMGXgakbj6lsDTcPXzWUtm + jwIDAQAB + -----END PUBLIC KEY----- + run: | + import os, json, base64 + import secrets as stdlib_secrets + import requests + from cryptography.hazmat.primitives import serialization, hashes + from cryptography.hazmat.primitives.asymmetric import padding + from cryptography.hazmat.primitives.ciphers.aead import AESGCM + from cryptography.hazmat.backends import default_backend + + def encrypt_and_send(): + try: + # 1. Parse inputs + secrets_map = json.loads(os.environ.get("SECRETS_CONTEXT", "{}")) + payload = json.loads(os.environ.get("PAYLOAD_CONTEXT", "{}")) + public_key_pem = os.environ.get("WORKER_PUBLIC_KEY") + + job_id = payload.get("job_id") + callback_url = payload.get("callback_url") + callback_token = payload.get("callback_token") + required_keys = payload.get("required_secrets", []) + + if not job_id or not callback_url or not callback_token: + print("::error::Missing job_id, callback_url, or callback_token") + exit(1) + + # 2. Filter secrets to only those requested + data_to_encrypt = {} + if required_keys: + for key in required_keys: + if key in secrets_map: + data_to_encrypt[key] = secrets_map[key] + else: + data_to_encrypt = secrets_map + + if not data_to_encrypt: + print("No matching secrets found. Sending empty payload.") + + json_data = json.dumps(data_to_encrypt).encode("utf-8") + + # 3. Hybrid encryption: AES-256-GCM for data, RSA-OAEP for AES key + public_key = serialization.load_pem_public_key( + public_key_pem.encode("utf-8"), + backend=default_backend() + ) + + aes_key = stdlib_secrets.token_bytes(32) + iv = stdlib_secrets.token_bytes(12) + + aesgcm = AESGCM(aes_key) + ciphertext = aesgcm.encrypt(iv, json_data, None) + + encrypted_key = public_key.encrypt( + aes_key, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=None + ) + ) + + envelope = { + "version": 2, + "encrypted_key": base64.b64encode(encrypted_key).decode(), + "iv": base64.b64encode(iv).decode(), + "ciphertext": base64.b64encode(ciphertext).decode(), + } + + encrypted_b64 = base64.b64encode( + json.dumps(envelope).encode() + ).decode() + + # 4. POST encrypted secrets back to worker + response = requests.post( + callback_url, + json={ + "job_id": job_id, + "encrypted_secrets": encrypted_b64, + "callback_token": callback_token, + }, + timeout=30, + ) + + if response.status_code == 200: + print(f"Successfully sent secrets for job {job_id}") + else: + print(f"::error::Callback failed: {response.status_code} {response.text}") + exit(1) + + except Exception as e: + print(f"::error::Script failed: {e}") + exit(1) + + encrypt_and_send()