File size: 3,277 Bytes
09aeaeb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
"""Explicit local module compatibility candidate; no target code execution."""
import base64
import hashlib
import json
import posixpath
import subprocess
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
sys.path[:0] = [str(ROOT / "packages/core")]
from chameleon_core.module_resolution import POLICY, resolve_relative_import
from chameleon_core.repository_capture import verify_package
from derived_overlay import validate_overlay


def parse_module_source(original):
    if len(original) > 262144:
        raise ValueError("MODULE_SOURCE_BUDGET")
    parsed = subprocess.run(["node", str(ROOT / "scripts/module_references.mjs")],
                            input=original, capture_output=True, timeout=10, check=False, cwd=ROOT)
    if parsed.returncode or len(parsed.stdout) > 100000:
        raise ValueError("MODULE_SYNTAX_UNSUPPORTED")
    evidence = json.loads(parsed.stdout)
    if evidence["source_sha256"] != hashlib.sha256(original).hexdigest() or evidence["dynamic_imports"]:
        raise ValueError("MODULE_DYNAMIC_OR_SOURCE_MISMATCH")
    return evidence


def build_module_overlay(package, importer, *, policy):
    if policy != POLICY:
        raise ValueError("EXPLICIT_MODULE_POLICY_REQUIRED")
    files = verify_package(package)
    if importer not in files or not importer.endswith((".js", ".mjs")):
        raise ValueError("CAPTURED_IMPORTER_REQUIRED")
    original = files[importer]
    if len(original) > 12000:
        raise ValueError("MODULE_OVERLAY_BUDGET")
    evidence = parse_module_source(original)
    inventory = {name: {"sha": record["git_blob_sha"], "size": record["bytes"]}
                 for name, record in package["repository"]["files"].items()}
    edits, plans = [], []
    for reference in evidence["references"]:
        plan = resolve_relative_import(importer, reference["specifier"], inventory)
        if plan["status"] != "RESOLVED":
            raise ValueError("MODULE_" + plan["status"])
        target = plan["matches"][0]["path"]
        relative = posixpath.relpath(target, posixpath.dirname(importer) or ".")
        if not relative.startswith("."):
            relative = "./" + relative
        start, end = reference["start_byte"], reference["end_byte"]
        if hashlib.sha256(original[start:end]).hexdigest() != reference["literal_sha256"]:
            raise ValueError("MODULE_LITERAL_BINDING")
        plans.append(plan)
        if relative != reference["specifier"]:
            edits.append((start, end, json.dumps(relative).encode()))
    if not edits:
        raise ValueError("MODULE_NO_REWRITE_NEEDED")
    after = original
    for start, end, replacement in reversed(edits):
        after = after[:start] + replacement + after[end:]
    overlay = {"version": "derived-file-v1", "source_digest": package["digest"], "path": importer,
               "before_sha256": hashlib.sha256(original).hexdigest(),
               "after_base64": base64.b64encode(after).decode(),
               "after_sha256": hashlib.sha256(after).hexdigest(), "operator": POLICY}
    validate_overlay(overlay, package)
    return {"overlay": overlay, "syntax": evidence, "resolutions": plans,
            "scope": "One-file explicit module policy; all behavior and delivery checks still required"}