o1js-scan 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
o1js_scan/__init__.py ADDED
@@ -0,0 +1,33 @@
1
+ """o1js-scan — a static soundness analyzer for o1js / Mina zkApps.
2
+
3
+ Public API:
4
+ analyze_file(filepath, source) -> list[Vulnerability]
5
+ analyze_project(root) -> list[(filepath, Vulnerability)]
6
+ is_o1js_source(content, path) -> bool
7
+ O1jsLexer -> the analyzer class
8
+ Severity, Vulnerability -> finding types
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from .lexer import (
14
+ O1JS_ORIGIN_TIER,
15
+ O1jsLexer,
16
+ analyze_file,
17
+ analyze_project,
18
+ is_o1js_source,
19
+ )
20
+ from .vuln import Severity, Vulnerability
21
+
22
+ __version__ = "0.1.0"
23
+
24
+ __all__ = [
25
+ "O1jsLexer",
26
+ "analyze_file",
27
+ "analyze_project",
28
+ "is_o1js_source",
29
+ "Severity",
30
+ "Vulnerability",
31
+ "O1JS_ORIGIN_TIER",
32
+ "__version__",
33
+ ]
o1js_scan/cli.py ADDED
@@ -0,0 +1,60 @@
1
+ """Command-line entry point for o1js-scan.
2
+
3
+ o1js-scan <path> [--json]
4
+
5
+ Exits 1 when any high/critical finding is present (CI-friendly), else 0.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import argparse
11
+ import json
12
+ import sys
13
+ from pathlib import Path
14
+ from typing import List, Optional
15
+
16
+ from .lexer import analyze_project
17
+
18
+
19
+ def main(argv: Optional[List[str]] = None) -> int:
20
+ ap = argparse.ArgumentParser(
21
+ prog="o1js-scan",
22
+ description="o1js (Mina/Kimchi) zkApp application-layer soundness scanner.",
23
+ )
24
+ ap.add_argument("path", help="file or directory to scan")
25
+ ap.add_argument("--json", action="store_true", help="emit JSONL findings")
26
+ args = ap.parse_args(argv)
27
+
28
+ # Fail loudly on a missing path. Otherwise a typo'd scan target silently
29
+ # produces zero findings and exit 0 — a green CI run that scanned nothing.
30
+ if not Path(args.path).exists():
31
+ print(f"o1js-scan: path not found: {args.path}", file=sys.stderr)
32
+ return 2
33
+
34
+ findings = analyze_project(args.path)
35
+
36
+ if args.json:
37
+ for fp, v in findings:
38
+ print(json.dumps({
39
+ "file": fp,
40
+ "rule_id": v.rule_id,
41
+ "severity": v.severity.value.lower(),
42
+ "function": v.function,
43
+ "line": (v.location or [0])[0],
44
+ "title": v.title,
45
+ "evidence": v.evidence,
46
+ }))
47
+ else:
48
+ if not findings:
49
+ print("o1js-scan: no findings (or no o1js files found)", file=sys.stderr)
50
+ for fp, v in findings:
51
+ line = (v.location or [0])[0]
52
+ print(f"{v.severity.value.upper():<8} {v.rule_id:<34} "
53
+ f"{Path(fp).name}:{line} fn={v.function} {v.title}")
54
+
55
+ hi = any(v.severity.value.lower() in ("critical", "high") for _f, v in findings)
56
+ return 1 if hi else 0
57
+
58
+
59
+ if __name__ == "__main__":
60
+ sys.exit(main())