|
69 | 69 | CONTRACT_CONFLICT_MARKER_PREFIX = "<!-- codex-pr-review-contract-conflict:" |
70 | 70 | AUTO_FIX_ALLOWED_MARKER_PREFIX = "<!-- codex-pr-review-auto-fix-allowed:" |
71 | 71 | NEXT_ACTION_MARKER_PREFIX = "<!-- codex-pr-review-next-action:" |
| 72 | +IMPLEMENTATION_MARKER_PREFIX = "<!-- codex-pr-review-implementation:v1:" |
| 73 | +IMPLEMENTATION_MARKER_SUFFIX = " -->" |
72 | 74 | DECISION_MARKER_SUFFIX = " -->" |
73 | 75 | FINDING_HISTORY_MAX_ROUNDS = 4 |
74 | 76 | FINDING_HISTORY_MAX_BYTES = 8192 |
@@ -366,6 +368,14 @@ def build_review_prompt(diff: str, pr_title: str, pr_body: str, repo: str) -> st |
366 | 368 | ) |
367 | 369 |
|
368 | 370 |
|
| 371 | +def review_implementation_digest() -> str: |
| 372 | + """Return the identity of the trusted bridge implementation that reviews a PR.""" |
| 373 | + digest = hashlib.sha256() |
| 374 | + for path in (Path(__file__), PROMPT_TEMPLATE_PATH): |
| 375 | + digest.update(path.read_bytes()) |
| 376 | + return digest.hexdigest()[:24] |
| 377 | + |
| 378 | + |
369 | 379 | def _truncate_lines(text: str, max_lines: int) -> str: |
370 | 380 | lines = text.splitlines() |
371 | 381 | if len(lines) <= max_lines: |
@@ -1291,6 +1301,7 @@ def build_pr_comment( |
1291 | 1301 | f"{CONTRACT_CONFLICT_MARKER_PREFIX}{str(bool(decision.get('contract_conflict'))).lower()}{DECISION_MARKER_SUFFIX}", |
1292 | 1302 | f"{AUTO_FIX_ALLOWED_MARKER_PREFIX}{str(bool(decision.get('auto_fix_allowed', True))).lower()}{DECISION_MARKER_SUFFIX}", |
1293 | 1303 | f"{NEXT_ACTION_MARKER_PREFIX}{decision.get('next_action', 'none')}{DECISION_MARKER_SUFFIX}", |
| 1304 | + f"{IMPLEMENTATION_MARKER_PREFIX}{review_implementation_digest()}{IMPLEMENTATION_MARKER_SUFFIX}", |
1294 | 1305 | "## 🤖 Codex PR Review", |
1295 | 1306 | "", |
1296 | 1307 | decision["summary"], |
@@ -1396,15 +1407,53 @@ def find_existing_review_comment( |
1396 | 1407 |
|
1397 | 1408 |
|
1398 | 1409 | def _is_trusted_review_comment(comment: Any) -> bool: |
1399 | | - """Accept review state only from the GitHub Actions identity that writes it.""" |
| 1410 | + """Accept state only from a complete trusted GitHub comment record.""" |
1400 | 1411 | if not isinstance(comment, dict): |
1401 | 1412 | return False |
1402 | 1413 | user = comment.get("user") |
1403 | 1414 | if not isinstance(user, dict): |
1404 | 1415 | return False |
1405 | 1416 | expected_login = env_value("CODEX_PR_REVIEW_COMMENT_AUTHOR", "github-actions[bot]").strip().casefold() |
1406 | 1417 | actual_login = str(user.get("login") or "").strip().casefold() |
1407 | | - return bool(expected_login and actual_login == expected_login) |
| 1418 | + if not expected_login or actual_login != expected_login: |
| 1419 | + return False |
| 1420 | + if str(user.get("type") or "").strip().casefold() != "bot": |
| 1421 | + return False |
| 1422 | + if not isinstance(comment.get("id"), int) or comment["id"] <= 0: |
| 1423 | + return False |
| 1424 | + if not isinstance(comment.get("created_at"), str) or not comment["created_at"].strip(): |
| 1425 | + return False |
| 1426 | + app = comment.get("performed_via_github_app") |
| 1427 | + if app is not None and ( |
| 1428 | + not isinstance(app, dict) |
| 1429 | + or str(app.get("slug") or "").strip().casefold() != "github-actions" |
| 1430 | + ): |
| 1431 | + return False |
| 1432 | + return True |
| 1433 | + |
| 1434 | + |
| 1435 | +def trusted_review_comment_provenance(comment: Any) -> str: |
| 1436 | + """Derive provenance from API record fields, never from comment markdown.""" |
| 1437 | + if not _is_trusted_review_comment(comment): |
| 1438 | + return "" |
| 1439 | + user = comment["user"] |
| 1440 | + record = { |
| 1441 | + "comment_id": comment["id"], |
| 1442 | + "author_id": user.get("id"), |
| 1443 | + "author_login": str(user.get("login") or "").casefold(), |
| 1444 | + "created_at": comment["created_at"], |
| 1445 | + "updated_at": comment.get("updated_at"), |
| 1446 | + } |
| 1447 | + raw = json.dumps(record, sort_keys=True, separators=(",", ":")).encode("utf-8") |
| 1448 | + return hashlib.sha256(raw).hexdigest()[:24] |
| 1449 | + |
| 1450 | + |
| 1451 | +def parse_review_implementation_digest(body: str) -> str: |
| 1452 | + match = re.search( |
| 1453 | + rf"{re.escape(IMPLEMENTATION_MARKER_PREFIX)}([0-9a-f]{{24}}){re.escape(IMPLEMENTATION_MARKER_SUFFIX)}", |
| 1454 | + body or "", |
| 1455 | + ) |
| 1456 | + return match.group(1) if match else "" |
1408 | 1457 |
|
1409 | 1458 |
|
1410 | 1459 | def parse_blocking_streak(body: str) -> int: |
|
0 commit comments