feat: complete Epic 1 production foundation
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
MAX_FILE_BYTES = 2 * 1024 * 1024
|
||||
FORBIDDEN = (
|
||||
(re.compile(r"\bcrank_adapter_rest\b|\bRestAdapterError\b"), "direct REST adapter dependency"),
|
||||
(re.compile(r"\breqwest\b"), "direct HTTP client dependency"),
|
||||
(
|
||||
re.compile(r"\.execute_with_(?:auth|context|auth_and_context)\s*\("),
|
||||
"legacy runtime execution bypass",
|
||||
),
|
||||
(
|
||||
re.compile(r"\.(?:execute_request|prepare_request|invoke_unary)\s*\("),
|
||||
"raw execution pipeline bypass",
|
||||
),
|
||||
(
|
||||
re.compile(r"\bpub\s+use\s+[^;]*(?:RuntimeExecutor|ProtocolAdapter)\b"),
|
||||
"execution boundary re-export",
|
||||
),
|
||||
(re.compile(r"\bsqlx\s*::"), "direct execution persistence dependency"),
|
||||
(
|
||||
re.compile(r"\.(?:validate_shape|apply_mapping|apply)\s*\("),
|
||||
"direct execution validation or mapping bypass",
|
||||
),
|
||||
)
|
||||
SCOPED_PREFIXES = (
|
||||
"apps/admin-api/src/routes/",
|
||||
"apps/admin-api/src/service/",
|
||||
"apps/admin-api/src/service.rs",
|
||||
)
|
||||
SCOPED_MCP_FILES = {
|
||||
"crates/crank-community-mcp/src/app.rs",
|
||||
"crates/crank-community-mcp/src/approval_execution.rs",
|
||||
"crates/crank-community-mcp/src/tool_error.rs",
|
||||
"crates/crank-community-mcp/src/app/invocation_history.rs",
|
||||
"crates/crank-community-mcp/src/app/tool_resolution.rs",
|
||||
"crates/crank-community-mcp/src/tool_search.rs",
|
||||
}
|
||||
SCOPED_MCP_PREFIXES = ("crates/crank-community-mcp/src/app/",)
|
||||
|
||||
|
||||
def parse_args(argv: list[str]) -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Check the canonical execution boundary.")
|
||||
parser.add_argument("--root", type=Path, default=Path.cwd())
|
||||
parser.add_argument("--files", nargs="*")
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
def candidate_paths(root: Path, explicit: list[str] | None) -> list[Path]:
|
||||
if explicit is None:
|
||||
result = subprocess.run(
|
||||
["git", "ls-files", "--cached", "--others", "--exclude-standard", "-z"],
|
||||
cwd=root,
|
||||
check=True,
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
names = [name for name in result.stdout.decode("utf-8").split("\0") if name]
|
||||
else:
|
||||
names = explicit
|
||||
return [root / name for name in sorted(set(names))]
|
||||
|
||||
|
||||
def logical_path(root: Path, path: Path) -> str:
|
||||
if path.is_absolute():
|
||||
resolved = path.resolve(strict=False)
|
||||
else:
|
||||
resolved = (root / path).resolve(strict=False)
|
||||
try:
|
||||
return resolved.relative_to(root.resolve()).as_posix()
|
||||
except ValueError as error:
|
||||
raise ValueError("path escapes repository root") from error
|
||||
|
||||
|
||||
def main(argv: list[str]) -> int:
|
||||
args = parse_args(argv)
|
||||
root = args.root.resolve()
|
||||
failures: list[str] = []
|
||||
try:
|
||||
paths = candidate_paths(root, args.files)
|
||||
except (OSError, subprocess.SubprocessError, UnicodeError) as error:
|
||||
print(f"error: execution boundary input unavailable ({type(error).__name__})", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
for supplied in paths:
|
||||
if supplied.is_symlink():
|
||||
failures.append("INVALID_PATH")
|
||||
continue
|
||||
try:
|
||||
logical = logical_path(root, supplied)
|
||||
except ValueError:
|
||||
failures.append("INVALID_PATH")
|
||||
continue
|
||||
if (
|
||||
not logical.endswith(".rs")
|
||||
or not (
|
||||
logical.startswith(SCOPED_PREFIXES)
|
||||
or logical.startswith(SCOPED_MCP_PREFIXES)
|
||||
or logical in SCOPED_MCP_FILES
|
||||
)
|
||||
):
|
||||
continue
|
||||
path = root / logical
|
||||
if path.is_symlink() or not path.is_file():
|
||||
failures.append(f"INVALID_PATH {logical[:256]}")
|
||||
continue
|
||||
if path.stat().st_size > MAX_FILE_BYTES:
|
||||
failures.append(f"INPUT_TOO_LARGE {logical[:256]}")
|
||||
continue
|
||||
try:
|
||||
source = path.read_text(encoding="utf-8")
|
||||
except (OSError, UnicodeError):
|
||||
failures.append(f"INVALID_SOURCE {logical[:256]}")
|
||||
continue
|
||||
for pattern, reason in FORBIDDEN:
|
||||
if pattern.search(source):
|
||||
failures.append(f"EXECUTION_BOUNDARY {logical[:256]} {reason}")
|
||||
|
||||
if failures:
|
||||
for failure in sorted(failures)[:1000]:
|
||||
print(f"error: {failure}", file=sys.stderr)
|
||||
return 1
|
||||
print("Execution boundary check passed")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main(sys.argv[1:]))
|
||||
Reference in New Issue
Block a user