--- runner/core.before +++ runner/core.after @@ -940,6 +940,14 @@ destination.mkdir() rows = [] excluded_sensitive = [] + aliases = [] + unresolved_aliases = [] + def whole_sha(path): + h = hashlib.sha256() + with path.open("rb") as stream: + for block in iter(lambda: stream.read(1024 * 1024), b""): + h.update(block) + return h.hexdigest() approved_roots = ((self.root / "runs").resolve(), Path(self.allocation(task_id)["build_dir"]).resolve()) def copy_file(source, target): require(source.is_file() and not source.is_symlink(), "ordinary review proof") @@ -949,13 +957,13 @@ require(not secret_path(str(resolved.relative_to(approved))), "secret-named review proof rejected") require(not target.exists(), "review proof target collision") target.parent.mkdir(parents=True, exist_ok=True) - before = hashlib.sha256(source.read_bytes()).hexdigest() + before = whole_sha(source) if sys.platform == "darwin": subprocess.run(["/bin/cp", "-pc", str(source), str(target)], check=True) else: shutil.copy2(source, target) - require(hashlib.sha256(target.read_bytes()).hexdigest() == before - and hashlib.sha256(source.read_bytes()).hexdigest() == before, + require(whole_sha(target) == before + and whole_sha(source) == before, "whole review proof mismatch or source changed") rows.append({"path": str(target.relative_to(worktree)), "original_path": str(source), "bytes": target.stat().st_size, "sha256": before}) @@ -976,13 +984,27 @@ require(build.resolve().is_relative_to(self.runtime(task_id).resolve()), "registered review build scope") if build.exists(): for source in sorted(build.rglob("*")): + if source.is_symlink(): + link = {"path": str(source.relative_to(build)), "target": os.readlink(source)} + try: + resolved = source.resolve(strict=True) + if not resolved.is_relative_to(build.resolve()) or secret_path(str(resolved.relative_to(build.resolve()))): + unresolved_aliases.append(dict(link, reason="outside registered nonsecret build")) + elif resolved.is_file(): + copy_file(resolved, destination / "build" / source.relative_to(build)) + aliases.append(dict(link, resolved_path=str(resolved), materialized_copy=True)) + else: + aliases.append(dict(link, resolved_path=str(resolved), directory_alias=True)) + except (OSError, RuntimeError) as exc: + unresolved_aliases.append(dict(link, reason=type(exc).__name__)) + continue if source.is_file(): if secret_path(str(source.relative_to(build))): excluded_sensitive.append(str(source.relative_to(build))) continue copy_file(source, destination / "build" / source.relative_to(build)) write_json(destination / "manifest.json", - {"candidate": revision, "task_id": task_id, "complete_nonsecret_registered_validation_material": True, "excluded_sensitive_paths": excluded_sensitive, "review_results_included": False, + {"candidate": revision, "task_id": task_id, "complete_nonsecret_registered_validation_material": not unresolved_aliases, "aliases": aliases, "unresolved_aliases": unresolved_aliases, "excluded_sensitive_paths": excluded_sensitive, "review_results_included": False, "file_count_limit": None, "byte_limit": None, "files": rows}) return {"path": ".review-material/manifest.json", "file_count": len(rows), "byte_limit": None}