saferun-cli 0.0.2__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.
Files changed (51) hide show
  1. saferun/__init__.py +3 -0
  2. saferun/__main__.py +5 -0
  3. saferun/cli.py +533 -0
  4. saferun/dockerdiag.py +65 -0
  5. saferun/doctor.py +159 -0
  6. saferun/engine/__init__.py +7 -0
  7. saferun/engine/analyzer.py +30 -0
  8. saferun/engine/context.py +27 -0
  9. saferun/engine/engine.py +100 -0
  10. saferun/engine/model.py +146 -0
  11. saferun/engine/node.py +145 -0
  12. saferun/engine/osv.py +291 -0
  13. saferun/engine/osvdb.py +367 -0
  14. saferun/engine/paths.py +41 -0
  15. saferun/engine/pysyntax.py +135 -0
  16. saferun/engine/rules/python.yml +103 -0
  17. saferun/engine/semgrep.py +173 -0
  18. saferun/engine/tools.py +167 -0
  19. saferun/engine/verdict.py +47 -0
  20. saferun/hostos/__init__.py +188 -0
  21. saferun/hostos/_win.py +316 -0
  22. saferun/hostos/acl.py +172 -0
  23. saferun/hostos/fsops.py +41 -0
  24. saferun/hostos/process.py +82 -0
  25. saferun/hostos/winfs.py +164 -0
  26. saferun/runcmd.py +139 -0
  27. saferun/sandbox/__init__.py +11 -0
  28. saferun/sandbox/analyzer.py +192 -0
  29. saferun/sandbox/attestation.py +110 -0
  30. saferun/sandbox/config.py +121 -0
  31. saferun/sandbox/controls.py +104 -0
  32. saferun/sandbox/copy.py +338 -0
  33. saferun/sandbox/copy_windows.py +281 -0
  34. saferun/sandbox/cosign_tool.py +134 -0
  35. saferun/sandbox/docker.py +167 -0
  36. saferun/sandbox/images.py +110 -0
  37. saferun/sandbox/profiles.py +40 -0
  38. saferun/sandbox/reaper.py +127 -0
  39. saferun/sandbox/runner.py +268 -0
  40. saferun/sandbox/secrets.py +91 -0
  41. saferun/sandbox/signature.py +117 -0
  42. saferun/sandbox/verify.py +75 -0
  43. saferun/sandbox/workspace.py +92 -0
  44. saferun/setup.py +559 -0
  45. saferun/ui.py +133 -0
  46. saferun_cli-0.0.2.dist-info/METADATA +136 -0
  47. saferun_cli-0.0.2.dist-info/RECORD +51 -0
  48. saferun_cli-0.0.2.dist-info/WHEEL +5 -0
  49. saferun_cli-0.0.2.dist-info/entry_points.txt +2 -0
  50. saferun_cli-0.0.2.dist-info/licenses/LICENSE +21 -0
  51. saferun_cli-0.0.2.dist-info/top_level.txt +1 -0
saferun/ui.py ADDED
@@ -0,0 +1,133 @@
1
+ """Terminal presentation only: colours, icons, a spinner and a progress bar. No logic that affects results lives here.
2
+
3
+ Everything is opt-in by terminal capability: colours need a TTY (or --color always) and respect NO_COLOR / FORCE_COLOR / TERM=dumb;
4
+ the spinner needs a TTY on the stream it writes to (stderr, so stdout and JSON stay clean); unicode icons fall back to ASCII on
5
+ non-UTF-8 terminals. When none of that applies the plain text output is byte-for-byte what it always was.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import itertools
10
+ import os
11
+ import shutil
12
+ import sys
13
+ import threading
14
+ import time
15
+
16
+ RESET = "\033[0m"
17
+ CODES = {"bold": "1", "dim": "2", "red": "31", "green": "32", "yellow": "33", "blue": "34", "magenta": "35", "cyan": "36", "white": "97",
18
+ "bg_green": "42", "bg_yellow": "43", "bg_red": "41", "bg_magenta": "45", "black": "30"}
19
+ ICONS_UNICODE = {"ok": "✓", "bad": "✗", "warn": "!", "skip": "–", "dot": "•", "arrow": "→", "bar_full": "█", "bar_empty": "░"}
20
+ ICONS_ASCII = {"ok": "+", "bad": "x", "warn": "!", "skip": "-", "dot": "*", "arrow": "->", "bar_full": "#", "bar_empty": "."}
21
+ SPIN_UNICODE = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
22
+ SPIN_ASCII = "|/-\\"
23
+ # verdict banner / severity tag styles
24
+ VERDICT_STYLE = {"PASS": ("black", "bold", "bg_green"), "WARNING": ("black", "bold", "bg_yellow"), "FAIL": ("white", "bold", "bg_red")}
25
+ SEVERITY_STYLE = {"critical": ("bold", "magenta"), "high": ("bold", "red"), "medium": ("yellow",), "unknown": ("yellow",), "low": ("cyan",), "info": ("dim",)}
26
+
27
+
28
+ BANNER = (
29
+ r" ____ __ ____ ",
30
+ r"/ ___| __ _ / _| ___| _ \ _ _ _ __ ",
31
+ r"\___ \ / _` | |_ / _ \ |_) | | | | '_ \ ",
32
+ r" ___) | (_| | _| __/ _ <| |_| | | | |",
33
+ r"|____/ \__,_|_| \___|_| \_\\__,_|_| |_|",
34
+ )
35
+ BANNER_TAGLINE = "Check AI-generated code before you run or trust it"
36
+
37
+
38
+ class Theme:
39
+ def __init__(self, color: bool = False, unicode_ok: bool = True):
40
+ self.on = color
41
+ self.icons = ICONS_UNICODE if unicode_ok else ICONS_ASCII
42
+ self.spin = SPIN_UNICODE if unicode_ok else SPIN_ASCII
43
+
44
+ def paint(self, text: str, *styles: str) -> str:
45
+ if not self.on or not styles:
46
+ return text
47
+ return "\033[" + ";".join(CODES[s] for s in styles) + "m" + text + RESET
48
+
49
+ def icon(self, name: str, *styles: str) -> str:
50
+ return self.paint(self.icons[name], *styles)
51
+
52
+
53
+ PLAIN = Theme(False, True)
54
+
55
+
56
+ def _truthy(value: str | None) -> bool:
57
+ return bool(value) and value != "0"
58
+
59
+
60
+ def theme_for(stream, mode: str = "auto") -> Theme:
61
+ """Colour policy: `never` -> off; `always` -> on; `auto` -> on only for a real terminal, unless NO_COLOR (any value) or TERM=dumb,
62
+ and on when FORCE_COLOR is set."""
63
+ enc = (getattr(stream, "encoding", "") or "").lower()
64
+ uni = enc.startswith("utf") and not _truthy(os.environ.get("SAFERUN_ASCII"))
65
+ if mode == "never":
66
+ return Theme(False, uni)
67
+ if mode == "always":
68
+ return Theme(True, uni)
69
+ if os.environ.get("NO_COLOR"):
70
+ return Theme(False, uni)
71
+ if _truthy(os.environ.get("FORCE_COLOR")):
72
+ return Theme(True, uni)
73
+ tty = bool(getattr(stream, "isatty", lambda: False)()) and os.environ.get("TERM", "") != "dumb"
74
+ return Theme(tty, uni)
75
+
76
+
77
+ def is_tty(stream) -> bool:
78
+ try:
79
+ return bool(stream.isatty()) and os.environ.get("TERM", "") != "dumb"
80
+ except (AttributeError, ValueError):
81
+ return False
82
+
83
+
84
+ class Spinner:
85
+ """`with Spinner(sys.stderr, "Scanning…"):` animates a line while the body runs. Does nothing unless the stream is a terminal.
86
+ Hides the cursor while spinning and always restores it and clears the line, also on Ctrl-C or an exception."""
87
+
88
+ def __init__(self, stream, message: str, theme: Theme | None = None, interval: float = 0.1):
89
+ self.stream, self.message, self.theme, self.interval = stream, message, theme or PLAIN, interval
90
+ self.enabled = is_tty(stream)
91
+ self._stop = threading.Event()
92
+ self._thread: threading.Thread | None = None
93
+ self._t0 = 0.0
94
+
95
+ def _run(self) -> None:
96
+ for frame in itertools.cycle(self.theme.spin):
97
+ if self._stop.is_set():
98
+ return
99
+ secs = int(time.monotonic() - self._t0)
100
+ room = max(10, shutil.get_terminal_size((100, 24)).columns - 10) # one physical line, or \r would leave debris
101
+ msg = self.message if len(self.message) <= room else self.message[: room - 1] + "…"
102
+ line = f"{self.theme.paint(frame, 'cyan', 'bold')} {msg} {self.theme.paint(f'{secs}s', 'dim')}"
103
+ self.stream.write("\r\033[K" + line)
104
+ self.stream.flush()
105
+ self._stop.wait(self.interval)
106
+
107
+ def __enter__(self):
108
+ if self.enabled:
109
+ self._t0 = time.monotonic()
110
+ self.stream.write("\033[?25l") # hide the cursor
111
+ self.stream.flush()
112
+ self._thread = threading.Thread(target=self._run, daemon=True)
113
+ self._thread.start()
114
+ return self
115
+
116
+ def __exit__(self, *exc) -> bool:
117
+ if self.enabled:
118
+ self._stop.set()
119
+ if self._thread:
120
+ self._thread.join(timeout=2)
121
+ self.stream.write("\r\033[K\033[?25h") # clear the line, show the cursor again
122
+ self.stream.flush()
123
+ return False
124
+
125
+
126
+ def bar(done: float, total: float | None, theme: Theme, width: int = 24) -> str:
127
+ """`[██████░░░░░░] 50%` (or `[####......] 50%`); an unknown total shows only the amount done by the caller."""
128
+ if not total or total <= 0:
129
+ return ""
130
+ frac = max(0.0, min(1.0, done / total))
131
+ n = int(round(frac * width))
132
+ body = theme.paint(theme.icons["bar_full"] * n, "cyan") + theme.paint(theme.icons["bar_empty"] * (width - n), "dim")
133
+ return f"[{body}] {int(frac * 100):3d}%"
@@ -0,0 +1,136 @@
1
+ Metadata-Version: 2.4
2
+ Name: saferun-cli
3
+ Version: 0.0.2
4
+ Summary: Secure verification of AI-generated code
5
+ Author: Vinay Repalle
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/vinay-repalle/saferun
8
+ Project-URL: Repository, https://github.com/vinay-repalle/saferun
9
+ Project-URL: Documentation, https://github.com/vinay-repalle/saferun/tree/main/docs
10
+ Project-URL: Issues, https://github.com/vinay-repalle/saferun/issues
11
+ Keywords: security,sandbox,docker,ai-generated-code,static-analysis,semgrep,osv,cosign,supply-chain
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Operating System :: POSIX :: Linux
19
+ Classifier: Operating System :: Microsoft :: Windows
20
+ Classifier: Operating System :: MacOS
21
+ Classifier: Topic :: Security
22
+ Classifier: Topic :: Software Development :: Quality Assurance
23
+ Requires-Python: >=3.12
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Provides-Extra: dev
27
+ Requires-Dist: pytest>=8; extra == "dev"
28
+ Requires-Dist: ruamel.yaml>=0.18; extra == "dev"
29
+ Requires-Dist: build>=1.2; extra == "dev"
30
+ Requires-Dist: twine>=5; extra == "dev"
31
+ Dynamic: license-file
32
+
33
+ # SafeRun
34
+
35
+ Checks AI-generated code before you run or trust it. Executing untrusted code in a sandbox **limits the impact** of that
36
+ code; it does **not** prove the code is safe, and a passing scan does not guarantee security.
37
+ Status: alpha (not independently certified). The signed analyzer image `analyzer-v1.0.1` is published from this repository and recorded in `ANALYZER_RELEASE`, so `saferun init` installs it after verifying its cosign signature; see [docs/release-images.md](https://github.com/vinay-repalle/saferun/blob/main/docs/release-images.md). Not yet done: a detection-quality benchmark, broader JavaScript rules, and verification of the Docker sandbox through Docker Desktop on Windows and macOS (see [docs/platforms.md](https://github.com/vinay-repalle/saferun/blob/main/docs/platforms.md)). **The CLI is the product**; `web/` is a static informational website.
38
+
39
+ ## Install in four commands
40
+ ```bash
41
+ pip install saferun-cli # the PyPI package is saferun-cli; the command it installs is `saferun`
42
+ saferun init # one-time setup: checks Docker, fetches a verified cosign, installs the signed analyzer image, downloads the OSV data
43
+ saferun doctor # read-only diagnosis: PASS / WARNING / FAIL per check, with the fix for each problem
44
+ saferun run my_script.py # scan the script; run it only inside the hardened sandbox if the scan allows it
45
+ ```
46
+ You do **not** install cosign or download the OSV database yourself. **Docker is the only prerequisite you install separately.**
47
+ `pipx install saferun-cli`, `uv tool install saferun-cli` and `poetry add saferun-cli` work as well. Requirements: Python 3.12 or newer, and for Poetry **version 2.0 or newer** (Poetry 1.x, including the 1.8 that Ubuntu 24.04 packages, cannot read this package's wheel metadata; use `pipx install poetry` or `pip install -U poetry`).
48
+
49
+ ## License
50
+ SafeRun is released under the [MIT License](https://github.com/vinay-repalle/saferun/blob/main/LICENSE) (Copyright (c) 2026 Vinay Repalle). The analyzer image also redistributes third-party software under its own licenses (Semgrep, OSV-Scanner and others); their notices are in [docker/analyzer/THIRD_PARTY_NOTICES.md](https://github.com/vinay-repalle/saferun/blob/main/docker/analyzer/THIRD_PARTY_NOTICES.md) and inside the image.
51
+
52
+ ## Key features
53
+ - **Restricted Docker sandbox** (Phase 1): read-only sanitized project copy, no network, non-root, no capabilities, resource limits, verified at run time.
54
+ - **Validation engine** (Phase 2): Python syntax check and `node --check` in the sandbox; Semgrep (Python) and an **offline** OSV-Scanner dependency scan, both run **only inside a verified, locked-down analyzer container** (Phase 5B, no host-side fallback); one normalized report and a PASS / WARNING / FAIL verdict.
55
+ - **CLI** (Phase 3): `saferun scan PATH`, text or JSON output, deterministic exit codes. Incomplete scans never report PASS.
56
+
57
+ ## Prerequisites
58
+ **Docker, running Linux containers and reachable by your user.** Remote Docker endpoints are refused (the project is bind-mounted by host path). The daemon must provide cgroup limits (memory, swap, pids, CPU) and seccomp.
59
+
60
+ | Platform | Install Docker | Notes |
61
+ |---|---|---|
62
+ | Linux | [Docker Engine](https://docs.docker.com/engine/install/); then `sudo usermod -aG docker $USER` and log in again | Fully tested platform. |
63
+ | macOS | [Docker Desktop](https://docs.docker.com/desktop/setup/install/mac-install/) (or Colima / OrbStack); start it once | Host code tested in CI; Docker Desktop integration not verified. |
64
+ | Windows 10/11 | [Docker Desktop](https://docs.docker.com/desktop/setup/install/windows-install/) with the WSL 2 backend, **Linux containers** mode | Native Windows host code, no WSL needed for SafeRun itself; Docker Desktop integration not verified. |
65
+
66
+ `saferun init` and `saferun doctor` tell you exactly which of these is wrong (Docker missing, daemon not running, permission denied, remote endpoint) and what to do. Details: [docs/platforms.md](https://github.com/vinay-repalle/saferun/blob/main/docs/platforms.md).
67
+
68
+ ## Install and set up
69
+ ```bash
70
+ pip install saferun-cli
71
+ saferun init # interactive: shows what is installed, offers the rest. For scripts/CI: saferun init --all (also --only, --skip, --yes, --repair)
72
+ saferun doctor # PASS / WARNING / FAIL, exit 1 if anything FAILED. --online also re-verifies the release signature; --format json for tools
73
+ saferun status # which components are installed and verified (exit 0 only if everything is ready)
74
+ ```
75
+ What `saferun init` does, in order, and what it never does:
76
+ 1. **Checks the host and Docker** (OS, installed, daemon running, permissions, local endpoint) and prints platform-specific guidance if something is missing. If Docker is unusable nothing is installed.
77
+ 2. **Pulls the sandbox images by pinned digest** (python, node) and re-checks the digest afterwards.
78
+ 3. **Installs the analyzer image** (Semgrep, OSV-Scanner, rules): it first obtains **cosign** by itself if you have none (a pinned release, downloaded over HTTPS into SafeRun's own data directory and accepted only if its SHA-256 equals the value written in SafeRun's source; it is re-hashed on every use), then verifies the image's **signature and identity** (this repository's release workflow on an `analyzer-v*` tag) **before anything is pulled**, pulls it by digest, checks digest, labels, tool versions and rules hash, and locks it. There is no unsigned install; `--build-local` is the explicit alternative and needs a source checkout.
79
+ 4. **Downloads and caches the OSV vulnerability data** (about 260 MB, public data from `osv-vulnerabilities.storage.googleapis.com`, nothing about your projects is sent) into your user data directory with integrity hashes. Only what is missing, invalid or older than 30 days is downloaded; a current database is left alone. Failed downloads are retried and never replace good data. If the data cannot be refreshed, `init` says how old the existing data is and scans report the dependency check as INCOMPLETE; **stale data is never treated as current**. Refresh later with `saferun osv-db update` (`--if-stale` to skip when it is current).
80
+ 5. **Verifies everything again** and prints a result per component.
81
+
82
+ `saferun init` skips what is already valid, so it is safe to re-run (for example after an analyzer release). Developers working from a checkout: `pip install -e '.[dev]'`, then `pytest -q` (Docker tests skip loudly without Docker; `SAFERUN_REQUIRE_DOCKER=1` makes them fail instead).
83
+
84
+ **Clean-environment install and validation, and what the CI runs:** [docs/install-and-test.md](https://github.com/vinay-repalle/saferun/blob/main/docs/install-and-test.md).
85
+
86
+ ## Quick start
87
+ ```bash
88
+ saferun scan ./my-project # human-readable report; nothing is executed
89
+ saferun scan ./my-project --format json # machine-readable report (schema_version 1)
90
+ saferun run my_script.py -- arg1 arg2 # scan first, then run inside the sandbox only if allowed
91
+ ```
92
+ Exit codes of `scan`: **0** PASS · **1** FAIL · **2** WARNING (including any incomplete scan) · **64** invalid usage/path · **70** fatal · **130** interrupted.
93
+
94
+ ### `saferun run`: what it does and what it guarantees
95
+ `saferun run SCRIPT [-- ARGS...]` scans the Python script with the normal checks and then runs it **only inside the same hardened Docker sandbox** used for checks: pinned `python:3.12-slim` image, **no network**, read-only root and project, non-root user, no capabilities, memory/CPU/pids/time limits, output cap, secrets redacted from output. It never runs anything on the host.
96
+ - Only the script itself is copied and scanned (`--project-dir DIR` scans and mounts a whole directory that contains it). Options go **before** the script name (`--timeout SECONDS`, default 30).
97
+ - **PASS** → it runs. **FAIL** → refused (exit 1). **WARNING** → refused (exit 2) by default. `--allow-warnings` accepts a WARNING only from a **complete** scan with no high/critical finding; an incomplete, errored, timed-out, skipped or unavailable check is never overridden, and neither is a FAIL.
98
+ - If the scan cannot be carried out (analyzer or Docker unusable, internal error) the script is **not** run (exit 70).
99
+ - A PASS means the checks found nothing they know about; it is **not** proof that the script is safe. The sandbox limits the impact of the code, it does not make it harmless. Scripts that need the network or third-party packages will fail inside it.
100
+ - Exit codes: **0** the script ran and exited 0 · **1** refused: FAIL · **2** refused: WARNING · **3** the script ran but exited non-zero, timed out or was killed · **64** usage · **70** could not scan or sandbox · **130** interrupted.
101
+
102
+ > **Privacy / network:** scans never use the network. Semgrep and OSV-Scanner run in a container without network access, and OSV-Scanner reads a local copy of the OSV data (`saferun osv-db update`, an explicit download of public data that contains nothing about your project). Without that data the dependency check reports `error` (verdict at best WARNING). Per-scan *online* OSV queries were removed in Phase 5B, see [PROJECT.md](https://github.com/vinay-repalle/saferun/blob/main/PROJECT.md).
103
+ >
104
+ > **Limits of the OSV data checks:** the download is checked against the server's MD5 header (refused if absent) and a SHA-256 is recorded locally and re-checked before every scan. That detects corruption and later modification of the files. It does **not** authenticate the download (the hash comes from the same server, or from your own disk), and someone who can write the data directory can rewrite the manifest too. Data older than 30 days makes the dependency check `incomplete`.
105
+
106
+ ## Website (static)
107
+ `web/` is a static informational site (what SafeRun is, features, security, CLI usage, GitHub link). It has no backend and does not scan anything.
108
+ ```bash
109
+ cd web && npm ci --ignore-scripts && npm test && npm run build # output: web/dist
110
+ ```
111
+ **Hosting:** Netlify. **Live site:** https://safe-run.netlify.app · **Repository:** https://github.com/vinay-repalle/saferun
112
+
113
+ | Netlify setting | Value |
114
+ |---|---|
115
+ | Deployment branch | `main` |
116
+ | Base directory | `web` |
117
+ | Build command | `npm ci --ignore-scripts && npm run build` |
118
+ | Publish directory | `dist` |
119
+ | Node.js | 22 |
120
+
121
+ Pages are switched by the URL hash, so no redirect rules are needed.
122
+
123
+ > **Scope:** the website is a static informational site only. SafeRun's CLI, sandbox and security analysis stay in this repository (`saferun/`, `docker/`) and run on your own machine; none of that is hosted by Netlify, and the site scans nothing.
124
+
125
+ ## Supported runtimes
126
+ | Runtime | Image | Status |
127
+ |---|---|---|
128
+ | Python | `python:3.12-slim` | sandbox, syntax check and Semgrep implemented |
129
+ | JavaScript / Node.js | `node:22-slim` | sandbox, `node --check` and npm lockfile scan implemented; **dependency installation, test runners and JS Semgrep rules are not implemented** |
130
+
131
+ ## Documentation
132
+ - [docs/cli.md](https://github.com/vinay-repalle/saferun/blob/main/docs/cli.md) — CLI options, exit codes, output examples
133
+ - [docs/engine.md](https://github.com/vinay-repalle/saferun/blob/main/docs/engine.md) — validation engine: checks, dependency coverage, statuses, verdict rules
134
+ - [docs/sandbox.md](https://github.com/vinay-repalle/saferun/blob/main/docs/sandbox.md) — sandbox library usage and guarantees
135
+ - [PROJECT.md](https://github.com/vinay-repalle/saferun/blob/main/PROJECT.md) — goals, architecture, threat model, known exposures (**read before relying on the sandbox**), phases
136
+ - [IMPLEMENTATION.md](https://github.com/vinay-repalle/saferun/blob/main/IMPLEMENTATION.md) — status, test evidence, limitations, technical notes
@@ -0,0 +1,51 @@
1
+ saferun/__init__.py,sha256=wxF3WJE-ftCiijf5W4ke1HS856cuE-rKBDKggQXRU0s,80
2
+ saferun/__main__.py,sha256=E6Gls0DNz8GQK2K-kOUIx8cYhgANW_CH54VKrfCfs14,52
3
+ saferun/cli.py,sha256=8j8DNmxrN-OZ0nH1AJkoT8n268eBiq7hxlFER96YsJ8,31659
4
+ saferun/dockerdiag.py,sha256=4YtgARF1ICghGgFFOmVq2SNjLG3RtBPAz8-fxftnG-M,3333
5
+ saferun/doctor.py,sha256=RKBbTPd54UaHuL3lYomu760ganf_ows-dQPMgzYXHTs,6922
6
+ saferun/runcmd.py,sha256=7vuXDtVFjPGKlL7bC5UCm0yfoWTiRqw8e8fIQG0342I,7236
7
+ saferun/setup.py,sha256=I4wlItAuoZmDIFkizmbPcJfqWIXDsEIA_SVm9YtO0-0,30186
8
+ saferun/ui.py,sha256=sbreyS-W5mjle6GJC00KGPMVi3CeXZNvsdQaaXJpv0w,5872
9
+ saferun/engine/__init__.py,sha256=jjzWrIFZZU30E3R9dRW5FJYg7sRmvMrh_rPzhZhWw98,459
10
+ saferun/engine/analyzer.py,sha256=W2IW1NEK1V12yGdYQc54Zul9Hem7uwNAPS4pu-v_MQI,2095
11
+ saferun/engine/context.py,sha256=bRI3A21Esi1qDpUZSkmWXpibHgOh6UDue2XqpAYYbes,1254
12
+ saferun/engine/engine.py,sha256=g3y-Mvd7RGpmJDaUE4z8uNTudej2NFVFTNYrOc9W8EM,5362
13
+ saferun/engine/model.py,sha256=SK1exaIxy1eXdJkSdrc1HpDq156XxVDW8Fpm1VUVg5A,6015
14
+ saferun/engine/node.py,sha256=yYvBUuR1aeb2mhy4bdOsibMKLWr8GriDwPtOgru9kJs,6821
15
+ saferun/engine/osv.py,sha256=42j1QwRYo56ilF27sr-K5qnNSwASjOhw_pBuDfDaiFU,16262
16
+ saferun/engine/osvdb.py,sha256=zeCLPk-TtG5490uAvCi4xvMUdh3ccPUAsz0e6nGD7Dw,18662
17
+ saferun/engine/paths.py,sha256=KXpgGNAGFqNNSpA2_S1QJie3KJ8WmVrfRt8J9J4POoA,1539
18
+ saferun/engine/pysyntax.py,sha256=gGcx94ldEt72kkNV5RjxyvVxxzhDM8XTEJgSbOeAFvU,6057
19
+ saferun/engine/semgrep.py,sha256=vbD4Qm89JhqhwlZLWHLfg1EVJie5VC5xqWVQf7vPk2A,9160
20
+ saferun/engine/tools.py,sha256=MN6CtPd9uqG49xgFb7KWbzG1Z-GO9J_VgSZ5NgNqjWA,6749
21
+ saferun/engine/verdict.py,sha256=LqkYtBOYujaMuuONIaZe18MTM5oNu2DIZ47ta5UHuE0,2466
22
+ saferun/engine/rules/python.yml,sha256=SkLAIrry8bnesyvjD6UOX-4c0XOK1YRrKaKH8kWW_bw,4120
23
+ saferun/hostos/__init__.py,sha256=gtVF6AwAAbjUU1lyL1mv6V2m41iml4p48LCSItKTVDw,8667
24
+ saferun/hostos/_win.py,sha256=z8eZgQpToXu0-fqkBcaoyZGeLL5-k7fnTEWK34r_caE,13623
25
+ saferun/hostos/acl.py,sha256=wqeNAN4q7X7Q6x4wAeP1JUAMpln8kmC9ZO8560-FRcM,7721
26
+ saferun/hostos/fsops.py,sha256=hwu-c06SHDlJxzLbCPAAPOWtAY4MzAjj6esn5OQ-SrA,2106
27
+ saferun/hostos/process.py,sha256=eo9Q0ZWzjKaVJkr5IFNmcjGbvE8taFYOoTv_Mb4pk9s,2443
28
+ saferun/hostos/winfs.py,sha256=G5XFdMFypRxL54kjo37HE0yQxa9BrzPVqz3RIaxuQUQ,7608
29
+ saferun/sandbox/__init__.py,sha256=X6AehI4IctBLVSNDqaHJqPUrMIH0oqD6bg9YLMWN7dE,565
30
+ saferun/sandbox/analyzer.py,sha256=IesgkeBMerH45ln1eaQrQ_VPrwabzxcXIRhW1wUzn9k,9044
31
+ saferun/sandbox/attestation.py,sha256=eDQq7VMoC7lOGzprXKa2ZqRcjFhpssMUyt6aqEI-PI4,5648
32
+ saferun/sandbox/config.py,sha256=HvUUjrkmmObCrLvFV1FVSBf19o1cHihmaV8HUAoUn1Q,4678
33
+ saferun/sandbox/controls.py,sha256=esoXLE34Mtj87Xty889fkKeewbS8UxHQwo8wdPLTr_k,6812
34
+ saferun/sandbox/copy.py,sha256=Rk40W8wo28v0LZJxygxZe_2td0CQZrokvWR6NydeWAM,15502
35
+ saferun/sandbox/copy_windows.py,sha256=CGscorkMZl1GsX89KX9pmYGJk8mpfe8yL5mprYcz3Z8,12634
36
+ saferun/sandbox/cosign_tool.py,sha256=sDCOGYj6F9pox062GDXvrMoZXCyVeZoEohk21n_CU5k,6137
37
+ saferun/sandbox/docker.py,sha256=JgQRe8IOhbarzzcQEpEY5icJGUG6ynjJx8FFdozwpU0,7276
38
+ saferun/sandbox/images.py,sha256=ujX0FEs5jFUyJfC0mFR4unFzmbRrvQYXe89TVwS964c,5788
39
+ saferun/sandbox/profiles.py,sha256=abDN6rqs2i8B37oUEXrBm4SVDugzwCEWKm9hQIoMY0M,1624
40
+ saferun/sandbox/reaper.py,sha256=9ZjQuKCmYLJK6ha6libtI4lGCuHsUJH8JUuc67xNmB0,5228
41
+ saferun/sandbox/runner.py,sha256=FkThjkRX9FPruPKczxkD7V5beD8YHhT8wB94tzaSzZc,11474
42
+ saferun/sandbox/secrets.py,sha256=LEPfJspTCH9WDtv5Yy5JjIJir4MSgsGvTek-qWWvRYw,4084
43
+ saferun/sandbox/signature.py,sha256=_dAUJagrvdpObIJzI6eZR7Hgc9YTRaElD8qIKWFZfX8,6463
44
+ saferun/sandbox/verify.py,sha256=kQmH_-LPBlktZoWVxfz-DAxBvGOU9b6ItJF2iPMOFio,3114
45
+ saferun/sandbox/workspace.py,sha256=go821YzpMKG7j7mGmFysFLnTTc_BKuNot-HC_D0xVqc,4017
46
+ saferun_cli-0.0.2.dist-info/licenses/LICENSE,sha256=neTkyPXACcHdbLtoAPCS09Bs9-ZatgCHEGoXJ5jwFkY,1070
47
+ saferun_cli-0.0.2.dist-info/METADATA,sha256=Hj4-GOpWvkwzB4zd9kPnJo_e3D1NRp8se803QpeoSF4,13066
48
+ saferun_cli-0.0.2.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
49
+ saferun_cli-0.0.2.dist-info/entry_points.txt,sha256=5K4q9kfsefC_LHrq3fq81VvSRpZ8p4fTI9nzhZQhUYg,45
50
+ saferun_cli-0.0.2.dist-info/top_level.txt,sha256=KWrtvCkcWH0OJMA7xjjgqkxmxEImXlWPxqv0c9dJltg,8
51
+ saferun_cli-0.0.2.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ saferun = saferun.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vinay Repalle
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ saferun