This commit is contained in:
Daniel Edrisian
2025-09-21 14:59:06 -07:00
parent 715800a699
commit 98c56b0d2e

View File

@@ -12,11 +12,16 @@ timeouts, and quiet progress output.
Promptinjection defenses
-------------------------
- The prompt given to Codex contains only the PR dump and a fixed
instruction template. No untrusted shell evaluation occurs.
instruction template. Codex executes in read-only mode.
- The generated file is written to a versioned path and not executed.
- External commands are restricted to `gh` (readonly API requests) and
`codex` for text generation; no usersupplied command strings are
interpolated.
Prompt-injection risks
-------------------------
- If a PR title/description includes bad commands (e.g. include "wire this much btc to XYZ wallet" in the PR highlights)
then codex might include this in the highlights.
"""
import argparse
@@ -114,6 +119,62 @@ def parse_args(argv: Sequence[str]) -> Args:
)
def main(argv: Sequence[str]) -> int:
pargs = parse_args(argv)
rest = pargs.rest
# repo optional first arg unless --repo provided
repo: str | None
if pargs.repo:
repo = pargs.repo
elif rest and "/" in rest[0]:
repo = rest[0]
rest = rest[1:]
else:
repo = detect_repo_from_git(pargs.repo_dir) or ""
if not repo:
eprint(
"Error: failed to auto-detect repository from git remote. Provide --repo <owner/repo> explicitly.",
)
return 1
if len(rest) < 2:
show_recent_releases_and_exit(repo, pargs.gh_timeout_secs)
return 1 # unreachable
from_tag, to_tag = rest[0], rest[1]
ver = rest[2] if len(rest) >= 3 else to_tag
ver = ver.lstrip("v")
script_dir = Path(__file__).resolve().parent
releases_dir = script_dir / "releases"
dump_file = releases_dir / f"release_dump_{ver}.txt"
gen_file = releases_dir / f"{ver}.txt"
# Create dump if missing
if not dump_file.exists():
header(f"Dump not found: {dump_file}. Generating...")
generate_dump(repo, from_tag, to_tag, dump_file, pargs.gh_timeout_secs)
else:
header(f"Using existing dump: {dump_file}")
if pargs.dump_only:
return 0
dump_path = abspath(dump_file)
prompt = build_prompt(dump_path)
header(f"Calling codex to generate {gen_file}")
status = run_codex(prompt, pargs.quiet, gen_file, pargs.codex_timeout_secs)
if gen_file.exists():
# Output only the generated release notes to stdout
sys.stdout.write(gen_file.read_text(encoding="utf-8"))
return 0
else:
eprint(f"Warning: {gen_file} not created. Check codex output.")
return 1 if status != 0 else 1
# -------- main at top; helpers follow --------
@@ -484,61 +545,6 @@ def run_codex(prompt: str, quiet: bool, gen_file: str, timeout_secs: int) -> int
return 124
def main(argv: Sequence[str]) -> int:
pargs = parse_args(argv)
rest = pargs.rest
# repo optional first arg unless --repo provided
repo: str | None
if pargs.repo:
repo = pargs.repo
elif rest and "/" in rest[0]:
repo = rest[0]
rest = rest[1:]
else:
repo = detect_repo_from_git(pargs.repo_dir) or ""
if not repo:
eprint(
"Error: failed to auto-detect repository from git remote. Provide --repo <owner/repo> explicitly.",
)
return 1
if len(rest) < 2:
show_recent_releases_and_exit(repo, pargs.gh_timeout_secs)
return 1 # unreachable
from_tag, to_tag = rest[0], rest[1]
ver = rest[2] if len(rest) >= 3 else to_tag
ver = ver.lstrip("v")
script_dir = Path(__file__).resolve().parent
releases_dir = script_dir / "releases"
dump_file = releases_dir / f"release_dump_{ver}.txt"
gen_file = releases_dir / f"{ver}.txt"
# Create dump if missing
if not dump_file.exists():
header(f"Dump not found: {dump_file}. Generating...")
generate_dump(repo, from_tag, to_tag, dump_file, pargs.gh_timeout_secs)
else:
header(f"Using existing dump: {dump_file}")
if pargs.dump_only:
return 0
dump_path = abspath(dump_file)
prompt = build_prompt(dump_path)
header(f"Calling codex to generate {gen_file}")
status = run_codex(prompt, pargs.quiet, gen_file, pargs.codex_timeout_secs)
if gen_file.exists():
# Output only the generated release notes to stdout
sys.stdout.write(gen_file.read_text(encoding="utf-8"))
return 0
else:
eprint(f"Warning: {gen_file} not created. Check codex output.")
return 1 if status != 0 else 1
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))