Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 33 additions & 6 deletions scripts/ci/pr_review_merge_scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import argparse
import json
import os
import re
import subprocess
import sys
from dataclasses import dataclass
Expand Down Expand Up @@ -78,6 +79,20 @@ class Decision:
reason: str


def validate_git_ref(ref: str) -> str:
"""Validate a git reference to prevent command injection."""
if not ref or ref.startswith("-") or not re.match(r"^[\w\-\.\/]+$", ref):
raise ValueError(f"Invalid git reference: {ref}")
return ref


def validate_git_sha(sha: str) -> str:
"""Validate a git commit SHA to prevent command injection."""
if not sha or not re.match(r"^[0-9a-fA-F]{40}$", sha):
raise ValueError(f"Invalid git SHA: {sha}")
return sha


def run(args: list[str], *, stdin: str | None = None) -> str:
"""Run a command and return stdout."""

Expand Down Expand Up @@ -224,7 +239,7 @@ def enable_auto_merge(repo: str, pr: dict[str, Any], *, dry_run: bool) -> None:
head = pr["headRefOid"]
if dry_run:
return
run(["gh", "pr", "merge", number, "--repo", repo, "--auto", "--merge", "--match-head-commit", head])
run(["gh", "pr", "merge", number, "--repo", repo, "--auto", "--merge", "--match-head-commit", validate_git_sha(head)])


def dispatch_opencode_review(repo: str, workflow: str, pr: dict[str, Any], *, dry_run: bool) -> None:
Expand All @@ -241,17 +256,17 @@ def dispatch_opencode_review(repo: str, workflow: str, pr: dict[str, Any], *, dr
"--repo",
repo,
"--ref",
pr["baseRefName"],
validate_git_ref(pr["baseRefName"]),
"-f",
f"pr_number={pr['number']}",
"-f",
f"pr_base_ref={pr['baseRefName']}",
f"pr_base_ref={validate_git_ref(pr['baseRefName'])}",
"-f",
f"pr_base_sha={pr['baseRefOid']}",
f"pr_base_sha={validate_git_sha(pr['baseRefOid'])}",
"-f",
f"pr_head_ref={pr['headRefName']}",
f"pr_head_ref={validate_git_ref(pr['headRefName'])}",
"-f",
f"pr_head_sha={pr['headRefOid']}",
f"pr_head_sha={validate_git_sha(pr['headRefOid'])}",
]
)

Expand Down Expand Up @@ -355,6 +370,18 @@ def self_test() -> None:
}
assert has_current_head_approval(sample)
assert not has_current_head_changes_requested(sample)
validate_git_ref("main")
validate_git_sha("a" * 40)
try:
validate_git_ref("-somebranch")
assert False, "Failed to reject invalid ref"
except ValueError:
pass
try:
validate_git_sha("branch; rm -rf /")
assert False, "Failed to reject invalid sha"
except ValueError:
pass
sample["reviews"]["nodes"].append(
{
"state": "CHANGES_REQUESTED",
Expand Down
4 changes: 4 additions & 0 deletions submit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import subprocess
import os

print("Submit mock called.")
Loading