vehlo-code-scanner 0.1.2__tar.gz → 0.1.4__tar.gz
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.
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/PKG-INFO +7 -4
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/README.md +6 -3
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/pyproject.toml +1 -1
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/cli/scan_command.py +74 -53
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/.gitignore +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/__init__.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/api/__init__.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/api/app.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/api/auth.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/api/deps.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/api/routes/__init__.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/api/routes/analytics.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/api/routes/auth.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/api/routes/findings.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/api/routes/groups.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/api/routes/health.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/api/routes/ingest.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/api/routes/overview.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/api/routes/repos.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/api/routes/scans.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/api/routes/tokens.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/api/schemas.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/api/services/__init__.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/api/services/auto_resolve.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/api/services/ingest.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/api/session.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/cli/__init__.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/cli/admin.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/cli/app.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/cli/output.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/client/__init__.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/client/api.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/config.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/db.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/enums.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/fingerprint.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/models/__init__.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/models/api_token.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/models/base.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/models/finding.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/models/finding_history.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/models/group.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/models/organization.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/models/repo.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/models/scan.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/models/user.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/scanner/__init__.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/scanner/models.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/scanner/parser.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/scanner/runner.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/services/__init__.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/services/provisioning.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/services/tokens.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/storage/__init__.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/storage/memory.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/storage/s3.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/worker/__init__.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/worker/celery_app.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/worker/db.py +0 -0
- {vehlo_code_scanner-0.1.2 → vehlo_code_scanner-0.1.4}/src/vcs/worker/tasks.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vehlo-code-scanner
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Multi-tenant security scanning platform wrapping Amazon Security Helper
|
|
5
5
|
Project-URL: Homepage, https://github.com/Vehlo-CyberSec/vehlo-code-scanner
|
|
6
6
|
Project-URL: Repository, https://github.com/Vehlo-CyberSec/vehlo-code-scanner
|
|
@@ -65,12 +65,15 @@ get the CLI, `--push`, and the client.
|
|
|
65
65
|
**Public (no AWS):**
|
|
66
66
|
|
|
67
67
|
```bash
|
|
68
|
-
# pip
|
|
68
|
+
# pipx (recommended — modern macOS/Linux pythons refuse bare pip installs)
|
|
69
|
+
pipx install 'vehlo-code-scanner[scan]' # quotes matter in zsh
|
|
70
|
+
|
|
71
|
+
# or pip, inside a venv
|
|
69
72
|
pip install 'vehlo-code-scanner[scan]'
|
|
70
73
|
|
|
71
|
-
# Docker from ECR Public (ASH bundled, nothing else to install)
|
|
74
|
+
# or Docker from ECR Public (ASH bundled, nothing else to install)
|
|
72
75
|
docker run --rm -v "$PWD:/src" \
|
|
73
|
-
public.ecr.aws
|
|
76
|
+
public.ecr.aws/w1f0w6h2/vehlo-code-scanner:latest scan /src --mode local
|
|
74
77
|
```
|
|
75
78
|
|
|
76
79
|
**AWS-gated (internal):** authenticate once with `aws sso login`, then:
|
|
@@ -37,12 +37,15 @@ get the CLI, `--push`, and the client.
|
|
|
37
37
|
**Public (no AWS):**
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
# pip
|
|
40
|
+
# pipx (recommended — modern macOS/Linux pythons refuse bare pip installs)
|
|
41
|
+
pipx install 'vehlo-code-scanner[scan]' # quotes matter in zsh
|
|
42
|
+
|
|
43
|
+
# or pip, inside a venv
|
|
41
44
|
pip install 'vehlo-code-scanner[scan]'
|
|
42
45
|
|
|
43
|
-
# Docker from ECR Public (ASH bundled, nothing else to install)
|
|
46
|
+
# or Docker from ECR Public (ASH bundled, nothing else to install)
|
|
44
47
|
docker run --rm -v "$PWD:/src" \
|
|
45
|
-
public.ecr.aws
|
|
48
|
+
public.ecr.aws/w1f0w6h2/vehlo-code-scanner:latest scan /src --mode local
|
|
46
49
|
```
|
|
47
50
|
|
|
48
51
|
**AWS-gated (internal):** authenticate once with `aws sso login`, then:
|
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import subprocess
|
|
6
|
-
import sys
|
|
7
6
|
from pathlib import Path
|
|
8
|
-
from typing import Optional
|
|
9
7
|
|
|
10
8
|
import typer
|
|
11
9
|
from rich.console import Console
|
|
@@ -19,66 +17,69 @@ console = Console()
|
|
|
19
17
|
SEVERITY_LEVELS = ["critical", "high", "medium", "low", "info"]
|
|
20
18
|
|
|
21
19
|
|
|
22
|
-
def
|
|
23
|
-
"""
|
|
20
|
+
def _git(args: list[str], cwd: str) -> str | None:
|
|
21
|
+
"""Run a git command, returning stripped stdout or None on failure."""
|
|
24
22
|
try:
|
|
25
|
-
|
|
26
|
-
["git",
|
|
27
|
-
cwd=
|
|
28
|
-
capture_output=True,
|
|
29
|
-
text=True,
|
|
30
|
-
check=True,
|
|
31
|
-
).stdout.strip()
|
|
32
|
-
branch = subprocess.run(
|
|
33
|
-
["git", "rev-parse", "--abbrev-ref", "HEAD"],
|
|
34
|
-
cwd=source_dir,
|
|
35
|
-
capture_output=True,
|
|
36
|
-
text=True,
|
|
37
|
-
check=True,
|
|
38
|
-
).stdout.strip()
|
|
39
|
-
remote_url = subprocess.run(
|
|
40
|
-
["git", "remote", "get-url", "origin"],
|
|
41
|
-
cwd=source_dir,
|
|
23
|
+
out = subprocess.run(
|
|
24
|
+
["git", *args],
|
|
25
|
+
cwd=cwd,
|
|
42
26
|
capture_output=True,
|
|
43
27
|
text=True,
|
|
44
28
|
check=True,
|
|
45
29
|
).stdout.strip()
|
|
46
30
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
47
|
-
return
|
|
31
|
+
return None
|
|
32
|
+
return out or None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _get_git_info(source_dir: str) -> tuple[str, str, str, str, str | None]:
|
|
36
|
+
"""Get commit SHA, branch, provider, org, and repo name from git.
|
|
37
|
+
|
|
38
|
+
Each field degrades independently: a repo without an `origin` remote
|
|
39
|
+
still reports its real SHA and branch, and vice versa. The repo name is
|
|
40
|
+
only known when a remote exists (callers fall back to the directory
|
|
41
|
+
name).
|
|
42
|
+
"""
|
|
43
|
+
sha = _git(["rev-parse", "HEAD"], source_dir) or "unknown"
|
|
44
|
+
branch = (
|
|
45
|
+
_git(["rev-parse", "--abbrev-ref", "HEAD"], source_dir) or "unknown"
|
|
46
|
+
)
|
|
47
|
+
remote_url = _git(["remote", "get-url", "origin"], source_dir)
|
|
48
|
+
if not remote_url:
|
|
49
|
+
return sha, branch, "github", "unknown", None
|
|
50
|
+
|
|
51
|
+
provider, org, repo = _parse_remote(remote_url)
|
|
52
|
+
return sha, branch, provider, org, repo
|
|
48
53
|
|
|
49
|
-
provider, org = _parse_remote(remote_url)
|
|
50
|
-
return sha, branch, provider, org
|
|
51
54
|
|
|
55
|
+
def _parse_remote(remote_url: str) -> tuple[str, str, str | None]:
|
|
56
|
+
"""Extract provider, org, and repo name from a git remote URL.
|
|
52
57
|
|
|
53
|
-
|
|
54
|
-
|
|
58
|
+
Handles git@host:org/repo.git, https://host/org/repo(.git), and
|
|
59
|
+
ssh://git@host/org/repo.git forms.
|
|
60
|
+
"""
|
|
55
61
|
url = remote_url.lower()
|
|
56
|
-
if "
|
|
57
|
-
provider = "github"
|
|
58
|
-
elif "bitbucket.org" in url:
|
|
62
|
+
if "bitbucket.org" in url:
|
|
59
63
|
provider = "bitbucket"
|
|
60
64
|
elif "gitlab.com" in url:
|
|
61
65
|
provider = "gitlab"
|
|
62
66
|
else:
|
|
63
67
|
provider = "github"
|
|
64
68
|
|
|
65
|
-
#
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if "
|
|
70
|
-
#
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
org = parts[-2]
|
|
80
|
-
|
|
81
|
-
return provider, org
|
|
69
|
+
# Normalize every form to host/org/.../repo, then read the tail.
|
|
70
|
+
path = remote_url
|
|
71
|
+
if "://" in path:
|
|
72
|
+
path = path.split("://", 1)[1] # strip scheme
|
|
73
|
+
if "@" in path:
|
|
74
|
+
path = path.split("@", 1)[1] # strip user
|
|
75
|
+
path = path.replace(":", "/", 1) # scp-like host:org/repo
|
|
76
|
+
parts = [p for p in path.removesuffix(".git").split("/") if p]
|
|
77
|
+
|
|
78
|
+
if len(parts) >= 3: # [host, org..., repo]
|
|
79
|
+
return provider, parts[-2], parts[-1]
|
|
80
|
+
if len(parts) == 2: # [host, org]
|
|
81
|
+
return provider, parts[1], None
|
|
82
|
+
return provider, "unknown", None
|
|
82
83
|
|
|
83
84
|
|
|
84
85
|
def _create_client(api_url: str, api_token: str) -> VCSClient:
|
|
@@ -91,7 +92,7 @@ def scan(
|
|
|
91
92
|
".",
|
|
92
93
|
help="Directory to scan. Defaults to current directory.",
|
|
93
94
|
),
|
|
94
|
-
scanners:
|
|
95
|
+
scanners: str | None = typer.Option(
|
|
95
96
|
None,
|
|
96
97
|
help="Comma-separated list of scanners to run (e.g., semgrep,bandit).",
|
|
97
98
|
),
|
|
@@ -99,7 +100,7 @@ def scan(
|
|
|
99
100
|
"container",
|
|
100
101
|
help="ASH execution mode: container (default, all scanners included), local, or precommit.",
|
|
101
102
|
),
|
|
102
|
-
fail_on:
|
|
103
|
+
fail_on: str | None = typer.Option(
|
|
103
104
|
None,
|
|
104
105
|
help=(
|
|
105
106
|
"Exit non-zero if findings at this severity or above exist "
|
|
@@ -110,16 +111,34 @@ def scan(
|
|
|
110
111
|
False,
|
|
111
112
|
help="Push results to the central API.",
|
|
112
113
|
),
|
|
113
|
-
api_url:
|
|
114
|
+
api_url: str | None = typer.Option(
|
|
114
115
|
None,
|
|
115
116
|
envvar="VCS_API_URL",
|
|
116
117
|
help="Central API URL (required with --push).",
|
|
117
118
|
),
|
|
118
|
-
api_token:
|
|
119
|
+
api_token: str | None = typer.Option(
|
|
119
120
|
None,
|
|
120
121
|
envvar="VCS_API_TOKEN",
|
|
121
122
|
help="API token for authentication (required with --push).",
|
|
122
123
|
),
|
|
124
|
+
repo_name: str | None = typer.Option(
|
|
125
|
+
None,
|
|
126
|
+
"--repo-name",
|
|
127
|
+
envvar="VCS_REPO_NAME",
|
|
128
|
+
help=(
|
|
129
|
+
"Repository name for attribution (defaults to the git remote's "
|
|
130
|
+
"repo name, then the directory name)."
|
|
131
|
+
),
|
|
132
|
+
),
|
|
133
|
+
org: str | None = typer.Option(
|
|
134
|
+
None,
|
|
135
|
+
"--org",
|
|
136
|
+
envvar="VCS_PROVIDER_ORG",
|
|
137
|
+
help=(
|
|
138
|
+
"Provider org/owner for attribution (defaults to the git "
|
|
139
|
+
"remote's org)."
|
|
140
|
+
),
|
|
141
|
+
),
|
|
123
142
|
) -> None:
|
|
124
143
|
"""Run a security scan against a directory using ASH."""
|
|
125
144
|
source_path = Path(source_dir).resolve()
|
|
@@ -154,14 +173,16 @@ def scan(
|
|
|
154
173
|
format_findings_table(result, console)
|
|
155
174
|
|
|
156
175
|
if push:
|
|
157
|
-
sha, branch, provider,
|
|
176
|
+
sha, branch, provider, git_org, git_repo = _get_git_info(
|
|
177
|
+
str(source_path)
|
|
178
|
+
)
|
|
158
179
|
client = _create_client(api_url, api_token)
|
|
159
180
|
try:
|
|
160
181
|
scan_id = client.push(
|
|
161
182
|
result=result,
|
|
162
183
|
repo_provider=provider,
|
|
163
|
-
repo_org=org,
|
|
164
|
-
repo_name=source_path.name,
|
|
184
|
+
repo_org=org or git_org,
|
|
185
|
+
repo_name=repo_name or git_repo or source_path.name,
|
|
165
186
|
commit_sha=sha,
|
|
166
187
|
branch=branch,
|
|
167
188
|
trigger_type="manual_cli",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|