File size: 2,799 Bytes
9c7fd58
b5fe4c7
 
 
9c7fd58
b5fe4c7
 
 
 
 
 
 
 
 
 
9c7fd58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b5fe4c7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9c7fd58
 
 
 
 
 
b5fe4c7
 
 
 
 
9c7fd58
 
b5fe4c7
 
 
 
 
 
 
 
 
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
70
71
72
73
74
75
"""Verify standalone model notices and reject broker code or plausible secrets."""

from __future__ import annotations

import hashlib
import re
import sys
import tarfile
import zipfile
from pathlib import Path

SECRET_PATTERNS = (
    re.compile(rb"sk-[A-Za-z0-9_-]{20,}"),
    re.compile(rb'(?i)(openai_api_key|anthropic_api_key)\s*["\'=:\s]+[A-Za-z0-9_-]{16,}'),
)
FORBIDDEN_MEMBERS = (
    "hermes_broker/",
    "deploy/",
    "reachy_mini_i_spy/auth.py",
    ".env",
    "config.json",
    "secrets.json",
)
DETECTOR_SUFFIX = "reachy_mini_i_spy/models/yolox_nano.onnx"
DETECTOR_SHA256 = "c789161ed43c8269fcd4e67c67eeeb4e80c622da2eb296a20bc6007bd18a0b7d"
REQUIRED_SUFFIXES = (
    DETECTOR_SUFFIX,
    "reachy_mini_i_spy/models/README.md",
    "reachy_mini_i_spy/models/YOLOX_LICENSE.txt",
)


def members(path: Path) -> list[tuple[str, bytes]]:
    if path.suffix == ".whl":
        with zipfile.ZipFile(path) as archive:
            return [(name, archive.read(name)) for name in archive.namelist() if not name.endswith("/")]
    with tarfile.open(path) as archive:
        result = []
        for member in archive.getmembers():
            handle = archive.extractfile(member) if member.isfile() else None
            if handle is not None:
                result.append((member.name, handle.read()))
        return result


def main() -> int:
    paths = [Path(value) for value in sys.argv[1:]]
    if not paths:
        raise SystemExit("usage: check_artifacts.py DIST...")
    failures: list[str] = []
    canary = __import__("os").environ.get("ISPY_SECRET_CANARY", "").encode()
    for path in paths:
        artifact_members = members(path)
        normalized_members = {name.replace("\\", "/"): data for name, data in artifact_members}
        for suffix in REQUIRED_SUFFIXES:
            if not any(name.endswith(suffix) for name in normalized_members):
                failures.append(f"{path}: required standalone asset missing: {suffix}")
        for name, data in normalized_members.items():
            normalized = name.replace("\\", "/")
            if any(part in normalized for part in FORBIDDEN_MEMBERS):
                failures.append(f"{path}: forbidden member {name}")
            if any(pattern.search(data) for pattern in SECRET_PATTERNS) or (canary and canary in data):
                failures.append(f"{path}: secret-like content in {name}")
            if normalized.endswith(DETECTOR_SUFFIX) and hashlib.sha256(data).hexdigest() != DETECTOR_SHA256:
                failures.append(f"{path}: local detector checksum mismatch")
    if failures:
        print("\n".join(failures), file=sys.stderr)
        return 1
    print(f"secret/package boundary check passed for {len(paths)} artifact(s)")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())