cherry_picker: validate commit IDs parsed from the issue body (#2816)

### What

Validate the commit IDs that `cherrypick_with_commits.py` parses out of
the issue body, accepting only git object names (7–40 hex characters).

### Why

The on-demand cherry-pick flow reads commit IDs from the `### Commit
IDs` section of the issue body:

```python
issue_body_dict["commits"] = get_middle_text(...).replace(" ", "").split(",")
```

That normalisation strips spaces and splits on commas, but it preserves
a leading dash. Each entry is then passed to git as an argv element:

```python
subprocess.run(["git", "diff-tree", "--no-commit-id", "--name-only", commit_id, "-r", "-m"])
```

Because `commit_id` sits in an argument position, a value such as
`--output=<path>` is interpreted by git as an *option* rather than a
revision. `git diff-tree` honours `--output=<file>` and
creates/truncates that file, so a malformed entry can write to a path on
the runner instead of being rejected as an invalid revision. Relative
paths are accepted, so the target is not confined to the checkout
directory.

The commit IDs come from the issue body, which is user-authored content,
so it seems worth constraining them to values git will only ever treat
as revisions.

The existing `subprocess` calls already use list form without
`shell=True`, so there is no shell involved here — this is purely about
git's own option parsing.

### How

Filter empty entries (so a trailing comma stays tolerated) and require
each remaining entry to match `^[0-9a-fA-F]{7,40}$`. Anything else is
reported back on the issue using the existing `issue_comment` helper,
following the error-reporting style already used in this script, and the
run exits cleanly.

Verified that the pattern accepts short (7) and full (40) hashes in
either case, and rejects `--output=...`, bare options such as `-r`,
over-short values, and non-hex strings.
1 file changed