sast 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.
- sast/__init__.py +9 -0
- sast/__main__.py +6 -0
- sast/_download.py +264 -0
- sast/cli.py +71 -0
- sast-0.1.0.dist-info/METADATA +129 -0
- sast-0.1.0.dist-info/RECORD +10 -0
- sast-0.1.0.dist-info/WHEEL +5 -0
- sast-0.1.0.dist-info/entry_points.txt +2 -0
- sast-0.1.0.dist-info/licenses/LICENSE +26 -0
- sast-0.1.0.dist-info/top_level.txt +1 -0
sast/__init__.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""sast — thin launcher for the Insomnia SAST binary.
|
|
2
|
+
|
|
3
|
+
`pip install sast` lays down only this tiny pure-Python launcher. The actual
|
|
4
|
+
SAST engine is a prebuilt, self-contained binary that is fetched on first run
|
|
5
|
+
from insom.ai, matched to your OS, checksum-verified, and cached. Subsequent
|
|
6
|
+
runs exec the cached binary directly.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
__version__ = "0.1.0"
|
sast/__main__.py
ADDED
sast/_download.py
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"""OS detection, manifest fetch, binary download + checksum + cache.
|
|
2
|
+
|
|
3
|
+
Pure stdlib (urllib/hashlib/platform) so the wheel has zero runtime deps and
|
|
4
|
+
stays a few KB. The hosted layout this expects on insom.ai:
|
|
5
|
+
|
|
6
|
+
https://insom.ai/static/downloads/sast/manifest.json
|
|
7
|
+
|
|
8
|
+
{
|
|
9
|
+
"version": "2026.06.04-abc1234",
|
|
10
|
+
"platforms": {
|
|
11
|
+
"linux": {"url": ".../sast-linux-x64", "sha256": "<hex>"},
|
|
12
|
+
"macos": {"url": ".../sast-macos-x64", "sha256": "<hex>"},
|
|
13
|
+
"windows": {"url": ".../sast-windows-x64.exe", "sha256": "<hex>"}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
`url` may be absolute or relative to the manifest's own URL. `sha256` is
|
|
18
|
+
optional but strongly recommended — when present it is enforced.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import hashlib
|
|
24
|
+
import os
|
|
25
|
+
import platform
|
|
26
|
+
import stat
|
|
27
|
+
import sys
|
|
28
|
+
import urllib.request
|
|
29
|
+
from urllib.parse import urljoin
|
|
30
|
+
|
|
31
|
+
# Override with the SAST_MANIFEST_URL env var (handy for staging / self-hosting).
|
|
32
|
+
DEFAULT_MANIFEST_URL = "https://insom.ai/static/downloads/sast/manifest.json"
|
|
33
|
+
|
|
34
|
+
_USER_AGENT = "sast-launcher"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class DownloadError(RuntimeError):
|
|
38
|
+
"""Raised when the binary cannot be fetched or verified."""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def manifest_url() -> str:
|
|
42
|
+
return os.environ.get("SAST_MANIFEST_URL", DEFAULT_MANIFEST_URL).strip()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def detect_platform() -> str:
|
|
46
|
+
"""Map the host OS to a manifest platform key (linux / macos / windows)."""
|
|
47
|
+
system = platform.system().lower()
|
|
48
|
+
if system.startswith("linux"):
|
|
49
|
+
return "linux"
|
|
50
|
+
if system == "darwin":
|
|
51
|
+
return "macos"
|
|
52
|
+
if system.startswith("win"):
|
|
53
|
+
return "windows"
|
|
54
|
+
raise DownloadError(
|
|
55
|
+
f"Unsupported operating system: {platform.system()!r}. "
|
|
56
|
+
"sast ships binaries for Linux, macOS and Windows only."
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _arch_is_supported() -> bool:
|
|
61
|
+
"""The hosted binaries are x86-64 only (for now). arm64 macs run via Rosetta."""
|
|
62
|
+
machine = platform.machine().lower()
|
|
63
|
+
return machine in {"x86_64", "amd64", "x64"} or platform.system().lower() == "darwin"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def cache_dir() -> str:
|
|
67
|
+
"""Per-user cache directory for the downloaded binary, by OS convention."""
|
|
68
|
+
override = os.environ.get("SAST_CACHE_DIR")
|
|
69
|
+
if override:
|
|
70
|
+
return override
|
|
71
|
+
system = platform.system().lower()
|
|
72
|
+
if system.startswith("win"):
|
|
73
|
+
base = os.environ.get("LOCALAPPDATA") or os.path.expanduser("~\\AppData\\Local")
|
|
74
|
+
return os.path.join(base, "sast", "bin")
|
|
75
|
+
if system == "darwin":
|
|
76
|
+
return os.path.expanduser("~/Library/Application Support/sast/bin")
|
|
77
|
+
base = os.environ.get("XDG_CACHE_HOME") or os.path.expanduser("~/.cache")
|
|
78
|
+
return os.path.join(base, "sast", "bin")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def binary_path() -> str:
|
|
82
|
+
name = "sast.exe" if detect_platform() == "windows" else "sast"
|
|
83
|
+
return os.path.join(cache_dir(), name)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _version_marker() -> str:
|
|
87
|
+
return os.path.join(cache_dir(), ".version")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _lastcheck_marker() -> str:
|
|
91
|
+
return os.path.join(cache_dir(), ".lastcheck")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _update_interval() -> int:
|
|
95
|
+
"""Seconds between background 'is a newer engine available?' checks.
|
|
96
|
+
|
|
97
|
+
Default 24h. Set SAST_UPDATE_INTERVAL=0 to disable auto-update entirely
|
|
98
|
+
(the cached binary is then used until `sast self-update` is run).
|
|
99
|
+
"""
|
|
100
|
+
raw = os.environ.get("SAST_UPDATE_INTERVAL", "").strip()
|
|
101
|
+
if not raw:
|
|
102
|
+
return 86400
|
|
103
|
+
try:
|
|
104
|
+
return max(0, int(raw))
|
|
105
|
+
except ValueError:
|
|
106
|
+
return 86400
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _update_check_due() -> bool:
|
|
110
|
+
interval = _update_interval()
|
|
111
|
+
if interval <= 0:
|
|
112
|
+
return False
|
|
113
|
+
import time
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
last = os.path.getmtime(_lastcheck_marker())
|
|
117
|
+
except OSError:
|
|
118
|
+
return True # never checked
|
|
119
|
+
return (time.time() - last) >= interval
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _mark_checked() -> None:
|
|
123
|
+
try:
|
|
124
|
+
os.makedirs(cache_dir(), exist_ok=True)
|
|
125
|
+
with open(_lastcheck_marker(), "w", encoding="utf-8") as fh:
|
|
126
|
+
fh.write("")
|
|
127
|
+
except OSError:
|
|
128
|
+
pass
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _http_get(url: str, *, binary: bool) -> bytes:
|
|
132
|
+
import ssl
|
|
133
|
+
|
|
134
|
+
req = urllib.request.Request(url, headers={"User-Agent": _USER_AGENT})
|
|
135
|
+
try:
|
|
136
|
+
with urllib.request.urlopen(req, timeout=120) as resp: # noqa: S310 (https only by default)
|
|
137
|
+
return resp.read()
|
|
138
|
+
except ssl.SSLCertVerificationError as exc:
|
|
139
|
+
hint = ""
|
|
140
|
+
if platform.system().lower() == "darwin":
|
|
141
|
+
# Classic python.org-build issue: the bundled OpenSSL has no CA store
|
|
142
|
+
# until the user runs the post-install "Install Certificates.command".
|
|
143
|
+
hint = (
|
|
144
|
+
"\nOn macOS this usually means your Python install has no CA "
|
|
145
|
+
"certificates. Run:\n"
|
|
146
|
+
' /Applications/Python\\ 3.x/Install\\ Certificates.command\n'
|
|
147
|
+
"or: pip install --upgrade certifi"
|
|
148
|
+
)
|
|
149
|
+
raise DownloadError(f"TLS certificate verification failed for {url}: {exc}{hint}") from exc
|
|
150
|
+
except Exception as exc: # urllib raises a zoo of exception types
|
|
151
|
+
raise DownloadError(f"Could not fetch {url}: {exc}") from exc
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _load_manifest() -> dict:
|
|
155
|
+
import json
|
|
156
|
+
|
|
157
|
+
raw = _http_get(manifest_url(), binary=False)
|
|
158
|
+
try:
|
|
159
|
+
data = json.loads(raw.decode("utf-8"))
|
|
160
|
+
except Exception as exc:
|
|
161
|
+
raise DownloadError(f"Manifest at {manifest_url()} is not valid JSON: {exc}") from exc
|
|
162
|
+
if not isinstance(data, dict) or "platforms" not in data:
|
|
163
|
+
raise DownloadError("Manifest is missing the required 'platforms' object.")
|
|
164
|
+
return data
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _verify_sha256(blob: bytes, expected: str) -> None:
|
|
168
|
+
actual = hashlib.sha256(blob).hexdigest()
|
|
169
|
+
if actual.lower() != expected.lower():
|
|
170
|
+
raise DownloadError(
|
|
171
|
+
"Checksum mismatch — refusing to install a tampered or corrupt binary.\n"
|
|
172
|
+
f" expected sha256: {expected}\n"
|
|
173
|
+
f" actual sha256: {actual}"
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def current_version() -> str | None:
|
|
178
|
+
try:
|
|
179
|
+
with open(_version_marker(), encoding="utf-8") as fh:
|
|
180
|
+
return fh.read().strip() or None
|
|
181
|
+
except OSError:
|
|
182
|
+
return None
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def ensure_binary(*, force: bool = False, quiet: bool = False) -> str:
|
|
186
|
+
"""Return the path to the cached binary, downloading it if needed.
|
|
187
|
+
|
|
188
|
+
Behaviour:
|
|
189
|
+
* missing binary -> download it (first run).
|
|
190
|
+
* force=True -> always re-fetch the latest (used by `sast self-update`).
|
|
191
|
+
* binary present -> reused immediately. At most once per
|
|
192
|
+
SAST_UPDATE_INTERVAL (default 24h) it also checks insom.ai for a newer
|
|
193
|
+
version and upgrades automatically. Network failures fail open: the
|
|
194
|
+
cached binary keeps working offline.
|
|
195
|
+
"""
|
|
196
|
+
path = binary_path()
|
|
197
|
+
exists = os.path.exists(path)
|
|
198
|
+
|
|
199
|
+
manifest = None
|
|
200
|
+
if exists and not force:
|
|
201
|
+
if not _update_check_due():
|
|
202
|
+
return path
|
|
203
|
+
# An update check is due — see whether insom.ai has a newer build.
|
|
204
|
+
try:
|
|
205
|
+
manifest = _load_manifest()
|
|
206
|
+
except DownloadError:
|
|
207
|
+
_mark_checked() # offline / server down: keep using the cached binary
|
|
208
|
+
return path
|
|
209
|
+
_mark_checked()
|
|
210
|
+
latest = manifest.get("version") or ""
|
|
211
|
+
if latest == (current_version() or ""):
|
|
212
|
+
return path # already on the latest
|
|
213
|
+
_warn(quiet, f"sast: newer engine available ({latest}); updating...")
|
|
214
|
+
|
|
215
|
+
if not _arch_is_supported():
|
|
216
|
+
_warn(
|
|
217
|
+
quiet,
|
|
218
|
+
f"warning: CPU architecture {platform.machine()!r} has no native sast build; "
|
|
219
|
+
"attempting the x86-64 binary.",
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
plat = detect_platform()
|
|
223
|
+
if manifest is None:
|
|
224
|
+
_warn(quiet, "sast: fetching the SAST engine (first run)..." if not force
|
|
225
|
+
else "sast: updating the SAST engine...")
|
|
226
|
+
manifest = _load_manifest()
|
|
227
|
+
|
|
228
|
+
entry = manifest.get("platforms", {}).get(plat)
|
|
229
|
+
if not entry or not entry.get("url"):
|
|
230
|
+
raise DownloadError(f"Manifest has no download entry for platform {plat!r}.")
|
|
231
|
+
|
|
232
|
+
url = urljoin(manifest_url(), entry["url"])
|
|
233
|
+
blob = _http_get(url, binary=True)
|
|
234
|
+
|
|
235
|
+
sha = entry.get("sha256")
|
|
236
|
+
if sha:
|
|
237
|
+
_verify_sha256(blob, sha)
|
|
238
|
+
else:
|
|
239
|
+
_warn(quiet, "warning: manifest provided no sha256 — skipping integrity check.")
|
|
240
|
+
|
|
241
|
+
os.makedirs(cache_dir(), exist_ok=True)
|
|
242
|
+
tmp = path + ".part"
|
|
243
|
+
with open(tmp, "wb") as fh:
|
|
244
|
+
fh.write(blob)
|
|
245
|
+
if plat != "windows":
|
|
246
|
+
mode = os.stat(tmp).st_mode
|
|
247
|
+
os.chmod(tmp, mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
|
|
248
|
+
os.replace(tmp, path)
|
|
249
|
+
|
|
250
|
+
version = manifest.get("version", "")
|
|
251
|
+
try:
|
|
252
|
+
with open(_version_marker(), "w", encoding="utf-8") as fh:
|
|
253
|
+
fh.write(version)
|
|
254
|
+
except OSError:
|
|
255
|
+
pass
|
|
256
|
+
_mark_checked()
|
|
257
|
+
|
|
258
|
+
_warn(quiet, f"sast: installed engine {version or '(unversioned)'} -> {path}")
|
|
259
|
+
return path
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _warn(quiet: bool, msg: str) -> None:
|
|
263
|
+
if not quiet:
|
|
264
|
+
print(msg, file=sys.stderr)
|
sast/cli.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""`sast` entry point: ensure the engine binary is present, then hand off to it.
|
|
2
|
+
|
|
3
|
+
All arguments after `sast` are passed straight through to the underlying SAST
|
|
4
|
+
binary, so `sast --help`, `sast <path> --sarif`, etc. behave exactly like the
|
|
5
|
+
native CLI. A few launcher-only subcommands are intercepted first.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import subprocess
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
from . import __version__
|
|
15
|
+
from ._download import (
|
|
16
|
+
DownloadError,
|
|
17
|
+
binary_path,
|
|
18
|
+
current_version,
|
|
19
|
+
ensure_binary,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
_LAUNCHER_HELP = f"""sast {__version__} — launcher for the Insomnia SAST engine
|
|
23
|
+
|
|
24
|
+
Usage:
|
|
25
|
+
sast [SAST-ARGS...] Run the SAST engine (downloads it on first use).
|
|
26
|
+
sast self-update Re-download the latest engine binary.
|
|
27
|
+
sast self-version Show launcher + cached-engine versions.
|
|
28
|
+
sast self-where Print the path to the cached engine binary.
|
|
29
|
+
|
|
30
|
+
Everything else is forwarded to the engine. Try: sast --help
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _run_engine(args: list[str]) -> int:
|
|
35
|
+
path = ensure_binary()
|
|
36
|
+
# On POSIX, exec replaces this process so signals/exit codes pass through
|
|
37
|
+
# cleanly. On Windows there is no exec, so spawn and forward the exit code.
|
|
38
|
+
if os.name == "posix":
|
|
39
|
+
os.execv(path, [path, *args]) # never returns
|
|
40
|
+
completed = subprocess.run([path, *args])
|
|
41
|
+
return completed.returncode
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def main(argv: list[str] | None = None) -> int:
|
|
45
|
+
argv = list(sys.argv[1:] if argv is None else argv)
|
|
46
|
+
|
|
47
|
+
if argv:
|
|
48
|
+
cmd = argv[0]
|
|
49
|
+
if cmd == "self-update":
|
|
50
|
+
ensure_binary(force=True)
|
|
51
|
+
return 0
|
|
52
|
+
if cmd == "self-version":
|
|
53
|
+
print(f"sast launcher {__version__}")
|
|
54
|
+
print(f"engine {current_version() or '(not yet installed)'}")
|
|
55
|
+
return 0
|
|
56
|
+
if cmd == "self-where":
|
|
57
|
+
print(binary_path())
|
|
58
|
+
return 0
|
|
59
|
+
if cmd in ("self-help", "--launcher-help"):
|
|
60
|
+
print(_LAUNCHER_HELP)
|
|
61
|
+
return 0
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
return _run_engine(argv)
|
|
65
|
+
except DownloadError as exc:
|
|
66
|
+
print(f"sast: {exc}", file=sys.stderr)
|
|
67
|
+
return 3
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
if __name__ == "__main__":
|
|
71
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sast
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: sast — free, fast static application security testing for CI/CD. Installs a self-contained SAST engine (17+ languages, taint tracking, secrets, IaC, SCA; HTML/JSON/SARIF) on first run.
|
|
5
|
+
Author: CQR Cybersecurity LLC
|
|
6
|
+
License: Proprietary
|
|
7
|
+
Project-URL: Homepage, https://insom.ai
|
|
8
|
+
Keywords: sast,security,static-analysis,sca,sarif,ci,devsecops
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Topic :: Security
|
|
13
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Requires-Python: >=3.8
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# sast
|
|
22
|
+
|
|
23
|
+
**Free, fast static application security testing for CI/CD.**
|
|
24
|
+
|
|
25
|
+
`sast` is a tiny launcher. Installing it is instant; the first time you run it,
|
|
26
|
+
it downloads a self-contained SAST engine binary that matches your operating
|
|
27
|
+
system, verifies its checksum, and caches it. Every run after that is native
|
|
28
|
+
speed with no Python dependencies.
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install sast
|
|
32
|
+
sast . # scan the current directory
|
|
33
|
+
sast ./src --sarif report.sarif
|
|
34
|
+
sast --help # full engine options
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
> Supports Linux, macOS and Windows (x86-64). On Apple Silicon the macOS
|
|
38
|
+
> binary runs under Rosetta.
|
|
39
|
+
|
|
40
|
+
## What it scans
|
|
41
|
+
|
|
42
|
+
- **SAST** across 17+ languages with taint tracking
|
|
43
|
+
- **Secrets** detection (entropy + vendor rule packs)
|
|
44
|
+
- **IaC / cloud misconfiguration** (Terraform, K8s, Docker, …)
|
|
45
|
+
- **SCA** — known-vulnerable dependencies
|
|
46
|
+
- Output as **HTML**, **JSON**, or **SARIF** (drops straight into GitHub code scanning)
|
|
47
|
+
|
|
48
|
+
## How it works
|
|
49
|
+
|
|
50
|
+
`pip install sast` lays down only a few KB of pure-Python launcher — **no
|
|
51
|
+
download happens at install time** (that keeps offline/CI installs reliable).
|
|
52
|
+
On first invocation the launcher:
|
|
53
|
+
|
|
54
|
+
1. Detects your OS → `linux` / `macos` / `windows`.
|
|
55
|
+
2. Fetches the manifest from `https://insom.ai/static/downloads/sast/manifest.json`.
|
|
56
|
+
3. Downloads the matching binary and verifies its `sha256`.
|
|
57
|
+
4. Caches it under your per-user cache directory and `exec`s it.
|
|
58
|
+
|
|
59
|
+
Because the engine lives on the server, new engine releases reach users
|
|
60
|
+
without republishing the pip package.
|
|
61
|
+
|
|
62
|
+
### Staying on the latest engine
|
|
63
|
+
|
|
64
|
+
After the first download the cached binary is reused for speed. At most once
|
|
65
|
+
per day (`SAST_UPDATE_INTERVAL`, default `86400` seconds) `sast` also asks
|
|
66
|
+
insom.ai whether a newer engine is published and, if so, upgrades itself
|
|
67
|
+
automatically. Update checks **fail open** — if you're offline or the server
|
|
68
|
+
is unreachable, the cached binary keeps working. Set `SAST_UPDATE_INTERVAL=0`
|
|
69
|
+
to pin the cached version, or run `sast self-update` to force the latest at
|
|
70
|
+
any time.
|
|
71
|
+
|
|
72
|
+
## Launcher commands
|
|
73
|
+
|
|
74
|
+
| Command | What it does |
|
|
75
|
+
|---------------------|-----------------------------------------------|
|
|
76
|
+
| `sast …` | Forward all args to the SAST engine |
|
|
77
|
+
| `sast self-update` | Re-download the latest engine binary |
|
|
78
|
+
| `sast self-version` | Show launcher + cached-engine versions |
|
|
79
|
+
| `sast self-where` | Print the cached binary path |
|
|
80
|
+
|
|
81
|
+
## Environment variables
|
|
82
|
+
|
|
83
|
+
| Variable | Purpose |
|
|
84
|
+
|-------------------------|------------------------------------------------------------|
|
|
85
|
+
| `SAST_MANIFEST_URL` | Override the manifest URL (staging / self-hosting) |
|
|
86
|
+
| `SAST_CACHE_DIR` | Override where the binary is cached |
|
|
87
|
+
| `SAST_UPDATE_INTERVAL` | Seconds between auto-update checks (default `86400`; `0` disables) |
|
|
88
|
+
|
|
89
|
+
Default cache locations:
|
|
90
|
+
|
|
91
|
+
- **Linux:** `~/.cache/sast/bin`
|
|
92
|
+
- **macOS:** `~/Library/Application Support/sast/bin`
|
|
93
|
+
- **Windows:** `%LOCALAPPDATA%\sast\bin`
|
|
94
|
+
|
|
95
|
+
## Use in CI (GitHub Actions)
|
|
96
|
+
|
|
97
|
+
```yaml
|
|
98
|
+
- run: pip install sast
|
|
99
|
+
- run: sast . --sarif results.sarif --fail-on high
|
|
100
|
+
- uses: github/codeql-action/upload-sarif@v3
|
|
101
|
+
with:
|
|
102
|
+
sarif_file: results.sarif
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`sast` exits non-zero when findings meet your `--fail-on` threshold, failing
|
|
106
|
+
the build.
|
|
107
|
+
|
|
108
|
+
## Server-side manifest format
|
|
109
|
+
|
|
110
|
+
The launcher expects this JSON at `SAST_MANIFEST_URL`:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"version": "2026.06.04-abc1234",
|
|
115
|
+
"platforms": {
|
|
116
|
+
"linux": { "url": "sast-linux-x64", "sha256": "<hex>" },
|
|
117
|
+
"macos": { "url": "sast-macos-x64", "sha256": "<hex>" },
|
|
118
|
+
"windows": { "url": "sast-windows-x64.exe", "sha256": "<hex>" }
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`url` may be relative to the manifest URL or absolute. `sha256` is optional but
|
|
124
|
+
enforced when present.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
© CQR Cybersecurity LLC. The `sast` launcher is open source; the SAST engine
|
|
129
|
+
binary it downloads is proprietary. See <https://insom.ai>.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
sast/__init__.py,sha256=h1EO8NTKoKdvOmoAv0AagYQHDe5-XOJuU1qzpjQAe5Y,354
|
|
2
|
+
sast/__main__.py,sha256=4ARGWRWoiMKKgxYtTsl3Vyl5Dy3yKdNaqkx3QNNyHAU,151
|
|
3
|
+
sast/_download.py,sha256=VfqocCiiDsBS5_bLfMzY45MdTPVoeP22iWCCD6HYasE,8808
|
|
4
|
+
sast/cli.py,sha256=fQnKFAmiF3RkqjT975DnKKSySAunwYkIMrh7URJjIMM,2147
|
|
5
|
+
sast-0.1.0.dist-info/licenses/LICENSE,sha256=Op0BssnKeTi7SZWQZDAYa9TQnTnTketj4RawE1nHW8g,1368
|
|
6
|
+
sast-0.1.0.dist-info/METADATA,sha256=0pCl3PLnbHnJWrw6kmkOH_4YuXKbRTD8u2WB4z0LCz4,4825
|
|
7
|
+
sast-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
8
|
+
sast-0.1.0.dist-info/entry_points.txt,sha256=F-3hNUrsGdBB3J6QAPHBRHHigKTpS0sM81ms42i-9G0,39
|
|
9
|
+
sast-0.1.0.dist-info/top_level.txt,sha256=M4oQT0X5raRTlKrbP6SBWFO-9ui8rLTNMCpQfniPHEQ,5
|
|
10
|
+
sast-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
MIT License (launcher only)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CQR Cybersecurity LLC
|
|
4
|
+
|
|
5
|
+
This license applies ONLY to the source code in this repository (the "sast"
|
|
6
|
+
launcher). It does NOT apply to the Insomnia SAST engine binary that the
|
|
7
|
+
launcher downloads at runtime, which is proprietary software of CQR
|
|
8
|
+
Cybersecurity LLC and is provided under separate terms.
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sast
|