mirror of
https://github.com/openai/codex.git
synced 2026-04-25 07:05:38 +00:00
## Summary - Pin Rust git patch dependencies to immutable revisions and make cargo-deny reject unknown git and registry sources unless explicitly allowlisted. - Add checked-in SHA-256 coverage for the current rusty_v8 release assets, wire those hashes into Bazel, and verify CI override downloads before use. - Add rusty_v8 MODULE.bazel update/check tooling plus a Bazel CI guard so future V8 bumps cannot drift from the checked-in checksum manifest. - Pin release/lint cargo installs and all external GitHub Actions refs to immutable inputs. ## Future V8 bump flow Run these after updating the resolved `v8` crate version and checksum manifest: ```bash python3 .github/scripts/rusty_v8_bazel.py update-module-bazel python3 .github/scripts/rusty_v8_bazel.py check-module-bazel ``` The update command rewrites the matching `rusty_v8_<crate_version>` `http_file` SHA-256 values in `MODULE.bazel` from `third_party/v8/rusty_v8_<crate_version>.sha256`. The check command is also wired into Bazel CI to block drift. ## Notes - This intentionally excludes RustSec dependency upgrades and bubblewrap-related changes per request. - The branch was rebased onto the latest origin/main before opening the PR. ## Validation - cargo fetch --locked - cargo deny check advisories - cargo deny check - cargo deny check sources - python3 .github/scripts/rusty_v8_bazel.py check-module-bazel - python3 .github/scripts/rusty_v8_bazel.py update-module-bazel - python3 -m unittest discover -s .github/scripts -p 'test_rusty_v8_bazel.py' - python3 -m py_compile .github/scripts/rusty_v8_bazel.py .github/scripts/rusty_v8_module_bazel.py .github/scripts/test_rusty_v8_bazel.py - repo-wide GitHub Actions `uses:` audit: all external action refs are pinned to 40-character SHAs - yq eval on touched workflows and local actions - git diff --check - just bazel-lock-check ## Hash verification - Confirmed `MODULE.bazel` hashes match `third_party/v8/rusty_v8_146_4_0.sha256`. - Confirmed GitHub release asset digests for denoland/rusty_v8 `v146.4.0` and openai/codex `rusty-v8-v146.4.0` match the checked-in hashes. - Streamed and SHA-256 hashed all 10 `MODULE.bazel` rusty_v8 asset URLs locally; every downloaded byte stream matched both `MODULE.bazel` and the checked-in manifest. ## Pin verification - Confirmed signing-action pins match the peeled commits for their tag comments: `sigstore/cosign-installer@v3.7.0`, `azure/login@v2`, and `azure/trusted-signing-action@v0`. - Pinned the remaining tag-based action refs in Bazel CI/setup: `actions/setup-node@v6`, `facebook/install-dotslash@v2`, `bazelbuild/setup-bazelisk@v3`, and `actions/cache/restore@v5`. - Normalized all `bazelbuild/setup-bazelisk@v3` refs to the peeled commit behind the annotated tag. - Audited Cargo git dependencies: every manifest git dependency uses `rev` only, every `Cargo.lock` git source has `?rev=<sha>#<same-sha>`, and `cargo deny check sources` passes with `required-git-spec = "rev"`. - Shallow-fetched each distinct git dependency repo at its pinned SHA and verified Git reports each object as a commit.
231 lines
7.5 KiB
Python
231 lines
7.5 KiB
Python
#!/usr/bin/env python3
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
|
|
|
|
SHA256_RE = re.compile(r"[0-9a-f]{64}")
|
|
HTTP_FILE_BLOCK_RE = re.compile(r"(?ms)^http_file\(\n.*?^\)\n?")
|
|
|
|
|
|
class RustyV8ChecksumError(ValueError):
|
|
pass
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class RustyV8HttpFile:
|
|
start: int
|
|
end: int
|
|
block: str
|
|
name: str
|
|
downloaded_file_path: str
|
|
sha256: str | None
|
|
|
|
|
|
def parse_checksum_manifest(path: Path) -> dict[str, str]:
|
|
try:
|
|
lines = path.read_text(encoding="utf-8").splitlines()
|
|
except FileNotFoundError as exc:
|
|
raise RustyV8ChecksumError(f"missing checksum manifest: {path}") from exc
|
|
|
|
checksums: dict[str, str] = {}
|
|
for line_number, line in enumerate(lines, 1):
|
|
if not line.strip():
|
|
continue
|
|
parts = line.split()
|
|
if len(parts) != 2:
|
|
raise RustyV8ChecksumError(
|
|
f"{path}:{line_number}: expected '<sha256> <filename>'"
|
|
)
|
|
checksum, filename = parts
|
|
if not SHA256_RE.fullmatch(checksum):
|
|
raise RustyV8ChecksumError(
|
|
f"{path}:{line_number}: invalid SHA-256 digest for {filename}"
|
|
)
|
|
if not filename or filename in {".", ".."} or "/" in filename:
|
|
raise RustyV8ChecksumError(
|
|
f"{path}:{line_number}: expected a bare artifact filename"
|
|
)
|
|
if filename in checksums:
|
|
raise RustyV8ChecksumError(
|
|
f"{path}:{line_number}: duplicate checksum for {filename}"
|
|
)
|
|
checksums[filename] = checksum
|
|
|
|
if not checksums:
|
|
raise RustyV8ChecksumError(f"empty checksum manifest: {path}")
|
|
return checksums
|
|
|
|
|
|
def string_field(block: str, field: str) -> str | None:
|
|
# Matches one-line string fields inside http_file blocks, e.g. `sha256 = "...",`.
|
|
match = re.search(rf'^\s*{re.escape(field)}\s*=\s*"([^"]+)",\s*$', block, re.M)
|
|
if match:
|
|
return match.group(1)
|
|
return None
|
|
|
|
|
|
def rusty_v8_http_files(module_bazel: str, version: str) -> list[RustyV8HttpFile]:
|
|
version_slug = version.replace(".", "_")
|
|
name_prefix = f"rusty_v8_{version_slug}_"
|
|
entries = []
|
|
for match in HTTP_FILE_BLOCK_RE.finditer(module_bazel):
|
|
block = match.group(0)
|
|
name = string_field(block, "name")
|
|
if not name or not name.startswith(name_prefix):
|
|
continue
|
|
downloaded_file_path = string_field(block, "downloaded_file_path")
|
|
if not downloaded_file_path:
|
|
raise RustyV8ChecksumError(
|
|
f"MODULE.bazel {name} is missing downloaded_file_path"
|
|
)
|
|
entries.append(
|
|
RustyV8HttpFile(
|
|
start=match.start(),
|
|
end=match.end(),
|
|
block=block,
|
|
name=name,
|
|
downloaded_file_path=downloaded_file_path,
|
|
sha256=string_field(block, "sha256"),
|
|
)
|
|
)
|
|
return entries
|
|
|
|
|
|
def module_entry_set_errors(
|
|
entries: list[RustyV8HttpFile],
|
|
checksums: dict[str, str],
|
|
version: str,
|
|
) -> list[str]:
|
|
errors = []
|
|
if not entries:
|
|
errors.append(f"MODULE.bazel has no rusty_v8 http_file entries for {version}")
|
|
return errors
|
|
|
|
module_files: dict[str, RustyV8HttpFile] = {}
|
|
duplicate_files = set()
|
|
for entry in entries:
|
|
if entry.downloaded_file_path in module_files:
|
|
duplicate_files.add(entry.downloaded_file_path)
|
|
module_files[entry.downloaded_file_path] = entry
|
|
|
|
for filename in sorted(duplicate_files):
|
|
errors.append(f"MODULE.bazel has duplicate http_file entries for {filename}")
|
|
|
|
for filename in sorted(set(module_files) - set(checksums)):
|
|
entry = module_files[filename]
|
|
errors.append(f"MODULE.bazel {entry.name} has no checksum in the manifest")
|
|
|
|
for filename in sorted(set(checksums) - set(module_files)):
|
|
errors.append(f"manifest has {filename}, but MODULE.bazel has no http_file")
|
|
|
|
return errors
|
|
|
|
|
|
def module_checksum_errors(
|
|
entries: list[RustyV8HttpFile],
|
|
checksums: dict[str, str],
|
|
) -> list[str]:
|
|
errors = []
|
|
for entry in entries:
|
|
expected = checksums.get(entry.downloaded_file_path)
|
|
if expected is None:
|
|
continue
|
|
if entry.sha256 is None:
|
|
errors.append(f"MODULE.bazel {entry.name} is missing sha256")
|
|
elif entry.sha256 != expected:
|
|
errors.append(
|
|
f"MODULE.bazel {entry.name} has sha256 {entry.sha256}, "
|
|
f"expected {expected}"
|
|
)
|
|
return errors
|
|
|
|
|
|
def raise_checksum_errors(message: str, errors: list[str]) -> None:
|
|
if errors:
|
|
formatted_errors = "\n".join(f"- {error}" for error in errors)
|
|
raise RustyV8ChecksumError(f"{message}:\n{formatted_errors}")
|
|
|
|
|
|
def check_module_bazel_text(
|
|
module_bazel: str,
|
|
checksums: dict[str, str],
|
|
version: str,
|
|
) -> None:
|
|
entries = rusty_v8_http_files(module_bazel, version)
|
|
errors = [
|
|
*module_entry_set_errors(entries, checksums, version),
|
|
*module_checksum_errors(entries, checksums),
|
|
]
|
|
raise_checksum_errors("rusty_v8 MODULE.bazel checksum drift", errors)
|
|
|
|
|
|
def block_with_sha256(block: str, checksum: str) -> str:
|
|
sha256_line_re = re.compile(r'(?m)^(\s*)sha256\s*=\s*"[0-9a-f]+",\s*$')
|
|
if sha256_line_re.search(block):
|
|
return sha256_line_re.sub(
|
|
lambda match: f'{match.group(1)}sha256 = "{checksum}",',
|
|
block,
|
|
count=1,
|
|
)
|
|
|
|
downloaded_file_path_match = re.search(
|
|
r'(?m)^(\s*)downloaded_file_path\s*=\s*"[^"]+",\n',
|
|
block,
|
|
)
|
|
if not downloaded_file_path_match:
|
|
raise RustyV8ChecksumError("http_file block is missing downloaded_file_path")
|
|
insert_at = downloaded_file_path_match.end()
|
|
indent = downloaded_file_path_match.group(1)
|
|
return f'{block[:insert_at]}{indent}sha256 = "{checksum}",\n{block[insert_at:]}'
|
|
|
|
|
|
def update_module_bazel_text(
|
|
module_bazel: str,
|
|
checksums: dict[str, str],
|
|
version: str,
|
|
) -> str:
|
|
entries = rusty_v8_http_files(module_bazel, version)
|
|
errors = module_entry_set_errors(entries, checksums, version)
|
|
raise_checksum_errors("cannot update rusty_v8 MODULE.bazel checksums", errors)
|
|
|
|
updated = []
|
|
previous_end = 0
|
|
for entry in entries:
|
|
updated.append(module_bazel[previous_end : entry.start])
|
|
updated.append(
|
|
block_with_sha256(entry.block, checksums[entry.downloaded_file_path])
|
|
)
|
|
previous_end = entry.end
|
|
updated.append(module_bazel[previous_end:])
|
|
return "".join(updated)
|
|
|
|
|
|
def check_module_bazel(
|
|
module_bazel_path: Path,
|
|
manifest_path: Path,
|
|
version: str,
|
|
) -> None:
|
|
checksums = parse_checksum_manifest(manifest_path)
|
|
module_bazel = module_bazel_path.read_text(encoding="utf-8")
|
|
check_module_bazel_text(module_bazel, checksums, version)
|
|
print(f"{module_bazel_path} rusty_v8 {version} checksums match {manifest_path}")
|
|
|
|
|
|
def update_module_bazel(
|
|
module_bazel_path: Path,
|
|
manifest_path: Path,
|
|
version: str,
|
|
) -> None:
|
|
checksums = parse_checksum_manifest(manifest_path)
|
|
module_bazel = module_bazel_path.read_text(encoding="utf-8")
|
|
updated_module_bazel = update_module_bazel_text(module_bazel, checksums, version)
|
|
if updated_module_bazel == module_bazel:
|
|
print(f"{module_bazel_path} rusty_v8 {version} checksums are already current")
|
|
return
|
|
module_bazel_path.write_text(updated_module_bazel, encoding="utf-8")
|
|
print(f"updated {module_bazel_path} rusty_v8 {version} checksums")
|