viur-cli 2.3.2__tar.gz → 3.0.0__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.
- {viur_cli-2.3.2/src/viur_cli.egg-info → viur_cli-3.0.0}/PKG-INFO +2 -2
- {viur_cli-2.3.2 → viur_cli-3.0.0}/pyproject.toml +6 -11
- {viur_cli-2.3.2 → viur_cli-3.0.0}/src/viur_cli/__init__.py +0 -1
- {viur_cli-2.3.2 → viur_cli-3.0.0}/src/viur_cli/cli.py +9 -9
- {viur_cli-2.3.2 → viur_cli-3.0.0}/src/viur_cli/conf.py +2 -2
- viur_cli-3.0.0/src/viur_cli/local.py +419 -0
- {viur_cli-2.3.2 → viur_cli-3.0.0}/src/viur_cli/scriptor/cli.py +9 -12
- viur_cli-3.0.0/src/viur_cli/update.py +213 -0
- viur_cli-3.0.0/src/viur_cli/version.py +2 -0
- {viur_cli-2.3.2 → viur_cli-3.0.0/src/viur_cli.egg-info}/PKG-INFO +2 -2
- {viur_cli-2.3.2 → viur_cli-3.0.0}/src/viur_cli.egg-info/SOURCES.txt +0 -2
- {viur_cli-2.3.2 → viur_cli-3.0.0}/src/viur_cli.egg-info/requires.txt +1 -1
- viur_cli-2.3.2/src/viur_cli/local.py +0 -227
- viur_cli-2.3.2/src/viur_cli/scriptor/login.py +0 -59
- viur_cli-2.3.2/src/viur_cli/tool.py +0 -80
- viur_cli-2.3.2/src/viur_cli/update.py +0 -133
- viur_cli-2.3.2/src/viur_cli/version.py +0 -2
- {viur_cli-2.3.2 → viur_cli-3.0.0}/LICENSE +0 -0
- {viur_cli-2.3.2 → viur_cli-3.0.0}/README.md +0 -0
- {viur_cli-2.3.2 → viur_cli-3.0.0}/setup.cfg +0 -0
- {viur_cli-2.3.2 → viur_cli-3.0.0}/src/viur_cli/build.py +0 -0
- {viur_cli-2.3.2 → viur_cli-3.0.0}/src/viur_cli/cloud.py +0 -0
- {viur_cli-2.3.2 → viur_cli-3.0.0}/src/viur_cli/deprecated.py +0 -0
- {viur_cli-2.3.2 → viur_cli-3.0.0}/src/viur_cli/package.py +0 -0
- {viur_cli-2.3.2 → viur_cli-3.0.0}/src/viur_cli/scriptor/__init__.py +0 -0
- {viur_cli-2.3.2 → viur_cli-3.0.0}/src/viur_cli/scripts/__init__.py +0 -0
- {viur_cli-2.3.2 → viur_cli-3.0.0}/src/viur_cli/scripts/get_pyodide.py +0 -0
- {viur_cli-2.3.2 → viur_cli-3.0.0}/src/viur_cli/setup.py +0 -0
- {viur_cli-2.3.2 → viur_cli-3.0.0}/src/viur_cli/utils.py +0 -0
- {viur_cli-2.3.2 → viur_cli-3.0.0}/src/viur_cli.egg-info/dependency_links.txt +0 -0
- {viur_cli-2.3.2 → viur_cli-3.0.0}/src/viur_cli.egg-info/entry_points.txt +0 -0
- {viur_cli-2.3.2 → viur_cli-3.0.0}/src/viur_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: viur-cli
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: Command-line interface for ViUR application maintenance.
|
|
5
5
|
Author-email: "Andreas H. Kelch" <ak@mausbrand.de>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -17,7 +17,7 @@ Requires-Dist: pipfile-requirements~=0.3
|
|
|
17
17
|
Requires-Dist: requests~=2.0
|
|
18
18
|
Requires-Dist: semver~=3.0
|
|
19
19
|
Requires-Dist: watchdog~=6.0
|
|
20
|
-
Requires-Dist:
|
|
20
|
+
Requires-Dist: pip-audit~=2.7
|
|
21
21
|
Requires-Dist: viur-scriptor-api~=1.0
|
|
22
22
|
Dynamic: license-file
|
|
23
23
|
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = [
|
|
3
|
-
"setuptools>=65",
|
|
4
|
-
"wheel"
|
|
5
|
-
]
|
|
2
|
+
requires = ["setuptools>=65", "wheel"]
|
|
6
3
|
build-backend = "setuptools.build_meta"
|
|
7
4
|
|
|
8
5
|
[project]
|
|
9
6
|
name = "viur-cli"
|
|
10
7
|
dynamic = ["version"]
|
|
11
|
-
authors = [
|
|
12
|
-
{name = "Andreas H. Kelch", email = "ak@mausbrand.de"}
|
|
13
|
-
]
|
|
8
|
+
authors = [{ name = "Andreas H. Kelch", email = "ak@mausbrand.de" }]
|
|
14
9
|
description = "Command-line interface for ViUR application maintenance."
|
|
15
10
|
readme = "README.md"
|
|
16
11
|
requires-python = ">=3.11"
|
|
@@ -26,7 +21,7 @@ dependencies = [
|
|
|
26
21
|
"requests~=2.0",
|
|
27
22
|
"semver~=3.0",
|
|
28
23
|
"watchdog~=6.0",
|
|
29
|
-
"
|
|
24
|
+
"pip-audit~=2.7",
|
|
30
25
|
"viur-scriptor-api~=1.0",
|
|
31
26
|
]
|
|
32
27
|
|
|
@@ -39,8 +34,8 @@ viur = "viur_cli:cli"
|
|
|
39
34
|
get-pyodide = "viur_cli.scripts.get_pyodide:main"
|
|
40
35
|
|
|
41
36
|
[tool.setuptools]
|
|
42
|
-
package-dir = {"" = "src"}
|
|
43
|
-
packages = {find = {where = ["src"]}}
|
|
37
|
+
package-dir = { "" = "src" }
|
|
38
|
+
packages = { find = { where = ["src"] } }
|
|
44
39
|
|
|
45
40
|
[tool.setuptools.dynamic]
|
|
46
|
-
version = {attr = "viur_cli.version.__version__"}
|
|
41
|
+
version = { attr = "viur_cli.version.__version__" }
|
|
@@ -3,7 +3,7 @@ import subprocess
|
|
|
3
3
|
import click
|
|
4
4
|
from .conf import *
|
|
5
5
|
from .version import __version__
|
|
6
|
-
from .version import
|
|
6
|
+
from .version import MINIMAL_UV
|
|
7
7
|
import semver
|
|
8
8
|
import pprint
|
|
9
9
|
import os
|
|
@@ -29,18 +29,18 @@ def cli(ctx):
|
|
|
29
29
|
"""
|
|
30
30
|
|
|
31
31
|
# Get the systems pipenv Version Number
|
|
32
|
-
|
|
32
|
+
uv_version = subprocess.check_output(['uv', 'self', 'version']).decode("utf-8")
|
|
33
33
|
version_pattern = r'\b(\d+\.\d+\.\d+)\b'
|
|
34
|
-
match = re.search(version_pattern,
|
|
35
|
-
|
|
34
|
+
match = re.search(version_pattern, uv_version)
|
|
35
|
+
sys_uv = match.group(1)
|
|
36
36
|
|
|
37
37
|
# sys kleiner min
|
|
38
|
-
if semver.compare(
|
|
38
|
+
if semver.compare(sys_uv, MINIMAL_UV) < 0:
|
|
39
39
|
echo_warning(
|
|
40
|
-
f"Your
|
|
41
|
-
f"This mismatch may cause Errors, please consider updating your Systems
|
|
42
|
-
f"Your Version: {
|
|
43
|
-
f"Recommended Version: {
|
|
40
|
+
f"Your uv Version does not match the recommended uv version. \n"
|
|
41
|
+
f"This mismatch may cause Errors, please consider updating your Systems uv version \n"
|
|
42
|
+
f"Your Version: {sys_uv}\n"
|
|
43
|
+
f"Recommended Version: {MINIMAL_UV}"
|
|
44
44
|
)
|
|
45
45
|
|
|
46
46
|
|
|
@@ -208,11 +208,11 @@ class ProjectConfig(Config):
|
|
|
208
208
|
|
|
209
209
|
|
|
210
210
|
def print_changelog_from_github(user, repo, last_version):
|
|
211
|
-
version_url = f"https://raw.githubusercontent.com/{user}/{repo}/main/CHANGELOG.md"
|
|
211
|
+
version_url = f"https://raw.githubusercontent.com/{user}/{repo}/refs/heads/main/CHANGELOG.md"
|
|
212
212
|
response = requests.get(version_url)
|
|
213
213
|
|
|
214
214
|
if last_version is not None:
|
|
215
|
-
version_url1 = f"https://raw.githubusercontent.com/{user}/{repo}/{last_version}/CHANGELOG.md"
|
|
215
|
+
version_url1 = f"https://raw.githubusercontent.com/{user}/{repo}/refs/tags/v{last_version}/CHANGELOG.md"
|
|
216
216
|
echo_warning(version_url1)
|
|
217
217
|
response1 = requests.get(version_url1)
|
|
218
218
|
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
import shutil
|
|
5
|
+
import subprocess
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
|
|
8
|
+
from viur_cli import echo_warning
|
|
9
|
+
from .conf import config
|
|
10
|
+
from . import cli, echo_error, utils, echo_fatal
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_user_info():
|
|
14
|
+
gcloud_auth_process = subprocess.run(
|
|
15
|
+
["gcloud", "auth", "application-default", "print-access-token"],
|
|
16
|
+
capture_output=True,
|
|
17
|
+
text=True,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
auth_token = gcloud_auth_process.stdout.strip() # Extract auth token
|
|
21
|
+
|
|
22
|
+
curl_command = f'curl -X GET -H "Authorization: Bearer {auth_token}" "https://www.googleapis.com/oauth2/v1/userinfo?alt=json"'
|
|
23
|
+
|
|
24
|
+
curl_process = subprocess.run(
|
|
25
|
+
curl_command, capture_output=True, shell=True, text=True
|
|
26
|
+
)
|
|
27
|
+
user_info = json.loads(curl_process.stdout)
|
|
28
|
+
|
|
29
|
+
return user_info
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@cli.command(context_settings={"ignore_unknown_options": True})
|
|
33
|
+
@click.argument("profile", default="default")
|
|
34
|
+
@click.argument("additional_args", nargs=-1)
|
|
35
|
+
def run(profile, additional_args):
|
|
36
|
+
"""
|
|
37
|
+
Start your application locally.
|
|
38
|
+
The 'run' command launches your ViUR application locally specified configuration and optional arguments.
|
|
39
|
+
This Enforces the Usage of gcloud tool
|
|
40
|
+
"""
|
|
41
|
+
try:
|
|
42
|
+
echo_warning(
|
|
43
|
+
f"You are using the development Server with your default account: {get_user_info()['email']}"
|
|
44
|
+
)
|
|
45
|
+
except:
|
|
46
|
+
echo_fatal(
|
|
47
|
+
f"It seems you are not Using an appropriate account. "
|
|
48
|
+
f"Please install the 'gcloud' tool or Log in with an appropriate account."
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
conf = config.get_profile(profile)
|
|
52
|
+
additional_args = list(additional_args)
|
|
53
|
+
|
|
54
|
+
if appyaml := conf.get("appyaml"):
|
|
55
|
+
additional_args.append(f"--appyaml={appyaml}")
|
|
56
|
+
if conf.get("port"):
|
|
57
|
+
additional_args.append(f"--port={conf['port']}")
|
|
58
|
+
if conf.get("gunicorn_port"):
|
|
59
|
+
additional_args.append(f"--gunicorn_port={conf['gunicorn_port']}")
|
|
60
|
+
|
|
61
|
+
utils.system(
|
|
62
|
+
f"app_server -A={conf['application_name']} {conf['distribution_folder']} {' '.join(additional_args)}"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@cli.command()
|
|
67
|
+
@click.argument("profile", default="default")
|
|
68
|
+
def env(profile):
|
|
69
|
+
"""
|
|
70
|
+
Check the local environment for ViUR development.
|
|
71
|
+
|
|
72
|
+
The 'env' command provides information about the versions tools and dependencies, such as ViUR-CLI, app_server,
|
|
73
|
+
git, Python, npm, node, and more. It checks the availability of these tools and reports their versions.
|
|
74
|
+
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
valid_icon = "\U00002714"
|
|
78
|
+
failed_icon = "\U0000274c"
|
|
79
|
+
|
|
80
|
+
conf = config.get_profile(profile)
|
|
81
|
+
click.echo(f"Project Info:\n--------------------------------")
|
|
82
|
+
try:
|
|
83
|
+
click.echo(f"format: {config['format']}")
|
|
84
|
+
for entry in conf["builds"]:
|
|
85
|
+
if entry in conf["builds"]:
|
|
86
|
+
click.echo(f"\n {entry}: {conf['builds'][entry]['version'] } ")
|
|
87
|
+
|
|
88
|
+
except Exception as e:
|
|
89
|
+
echo_error("Error while collecting viur info")
|
|
90
|
+
|
|
91
|
+
echo_error(str(e))
|
|
92
|
+
click.echo(f"\nCurrent Environment:\n--------------------------------")
|
|
93
|
+
|
|
94
|
+
# viur-cli
|
|
95
|
+
if shutil.which("viur"):
|
|
96
|
+
app_server_version = subprocess.check_output(["viur", "--version"]).decode(
|
|
97
|
+
"utf-8"
|
|
98
|
+
)
|
|
99
|
+
click.echo(f"{valid_icon} {app_server_version}")
|
|
100
|
+
else:
|
|
101
|
+
click.echo(f"{failed_icon} ViUR-CLI")
|
|
102
|
+
|
|
103
|
+
# app_server
|
|
104
|
+
if shutil.which("app_server"):
|
|
105
|
+
app_server_version = subprocess.check_output(["app_server", "-V"]).decode(
|
|
106
|
+
"utf-8"
|
|
107
|
+
)
|
|
108
|
+
click.echo(f"{valid_icon} {app_server_version}")
|
|
109
|
+
else:
|
|
110
|
+
click.echo(f"{failed_icon} app_server")
|
|
111
|
+
|
|
112
|
+
# git
|
|
113
|
+
if shutil.which("git"):
|
|
114
|
+
git_version = subprocess.check_output(["git", "--version"]).decode("utf-8")
|
|
115
|
+
click.echo(f"{valid_icon} {git_version}")
|
|
116
|
+
else:
|
|
117
|
+
click.echo(f"{failed_icon}")
|
|
118
|
+
|
|
119
|
+
# python3
|
|
120
|
+
if shutil.which("python3"):
|
|
121
|
+
npm_version = subprocess.check_output(["python3", "-V"]).decode("utf-8")
|
|
122
|
+
click.echo(f"{valid_icon} python3 > {npm_version}")
|
|
123
|
+
else:
|
|
124
|
+
click.echo(f"{failed_icon}")
|
|
125
|
+
|
|
126
|
+
# python
|
|
127
|
+
if shutil.which("python"):
|
|
128
|
+
npm_version = subprocess.check_output(["python", "-V"]).decode("utf-8")
|
|
129
|
+
click.echo(f"{valid_icon} python > {npm_version}")
|
|
130
|
+
else:
|
|
131
|
+
click.echo(f"{failed_icon}")
|
|
132
|
+
|
|
133
|
+
# python3
|
|
134
|
+
if shutil.which("pyenv"):
|
|
135
|
+
pyenv_version = subprocess.check_output(["pyenv", "--version"]).decode("utf-8")
|
|
136
|
+
click.echo(f"{valid_icon} {pyenv_version}")
|
|
137
|
+
else:
|
|
138
|
+
click.echo(f"{failed_icon}")
|
|
139
|
+
|
|
140
|
+
# npm
|
|
141
|
+
if shutil.which("npm"):
|
|
142
|
+
npm_version = subprocess.check_output(["npm", "-v"]).decode("utf-8")
|
|
143
|
+
click.echo(f"{valid_icon} npm {npm_version}")
|
|
144
|
+
else:
|
|
145
|
+
click.echo(f"{failed_icon} npm")
|
|
146
|
+
|
|
147
|
+
# node
|
|
148
|
+
if shutil.which("node"):
|
|
149
|
+
npm_version = subprocess.check_output(["node", "-v"]).decode("utf-8")
|
|
150
|
+
click.echo(f"{valid_icon} node {npm_version}")
|
|
151
|
+
else:
|
|
152
|
+
click.echo(f"{failed_icon} node")
|
|
153
|
+
|
|
154
|
+
# pnpm
|
|
155
|
+
if shutil.which("pnpm"):
|
|
156
|
+
npm_version = subprocess.check_output(["pnpm", "-v"]).decode("utf-8")
|
|
157
|
+
click.echo(f"{valid_icon} pnpm {npm_version}")
|
|
158
|
+
else:
|
|
159
|
+
click.echo(f"{failed_icon} pnpm (optional)")
|
|
160
|
+
|
|
161
|
+
# gcloud
|
|
162
|
+
if shutil.which("gcloud"):
|
|
163
|
+
gcloud_version = (
|
|
164
|
+
subprocess.check_output(["gcloud", "-v"]).decode("utf-8").split("\n\n")[0]
|
|
165
|
+
)
|
|
166
|
+
versionList = []
|
|
167
|
+
for line in gcloud_version.split("\n"):
|
|
168
|
+
if not line:
|
|
169
|
+
continue
|
|
170
|
+
if not line.startswith("Google Cloud SDK"):
|
|
171
|
+
line = " - " + line
|
|
172
|
+
versionList.append(line)
|
|
173
|
+
versionString = "\n".join(versionList)
|
|
174
|
+
click.echo(f"{valid_icon} {versionString}")
|
|
175
|
+
else:
|
|
176
|
+
click.echo(f"{failed_icon} gcloud")
|
|
177
|
+
|
|
178
|
+
click.echo(f"\nYour default gcloud user Info:\n--------------------------------")
|
|
179
|
+
for k, v in get_user_info().items():
|
|
180
|
+
click.echo(f"{k}: {v}")
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@cli.command()
|
|
184
|
+
@click.option("--dev", "-d", is_flag=True, default=False)
|
|
185
|
+
def check(dev):
|
|
186
|
+
"""
|
|
187
|
+
Perform security checks for vulnerabilities.
|
|
188
|
+
The 'check' command helps you identify and address security vulnerabilities in your project's dependencies.
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
if do_checks(dev):
|
|
192
|
+
utils.echo_info("\U00002714 No vulnerabilities found.")
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def do_checks(dev=True):
|
|
196
|
+
"""
|
|
197
|
+
Run security checks for Python and npm dependencies.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
dev: Development mode flag (currently unused but kept for API compatibility)
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
bool: True if no vulnerabilities found, False otherwise
|
|
204
|
+
"""
|
|
205
|
+
has_vulnerabilities = False
|
|
206
|
+
|
|
207
|
+
# Python vulnerability check
|
|
208
|
+
try:
|
|
209
|
+
# Run uv-secure with JSON output to parse results
|
|
210
|
+
result = subprocess.run(
|
|
211
|
+
[
|
|
212
|
+
"uvx",
|
|
213
|
+
"pysentry-rs",
|
|
214
|
+
"--sources",
|
|
215
|
+
"pypa,pypi,osv",
|
|
216
|
+
"--format",
|
|
217
|
+
"json",
|
|
218
|
+
"--fail-on",
|
|
219
|
+
"high",
|
|
220
|
+
],
|
|
221
|
+
capture_output=True,
|
|
222
|
+
check=True,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
# Parse JSON output (strict=False allows control characters)
|
|
226
|
+
data = json.loads(result.stdout, strict=False)
|
|
227
|
+
|
|
228
|
+
# Check if no vulnerabilities found
|
|
229
|
+
if data.get("vulnerable_packages", 0) == 0:
|
|
230
|
+
py_vulns = False
|
|
231
|
+
else:
|
|
232
|
+
py_vulns = True
|
|
233
|
+
has_vulnerabilities = True
|
|
234
|
+
|
|
235
|
+
# Format scan time to human readable format
|
|
236
|
+
scan_time_str = data.get("scan_time", "N/A")
|
|
237
|
+
try:
|
|
238
|
+
scan_time = datetime.fromisoformat(scan_time_str.replace("Z", "+00:00"))
|
|
239
|
+
formatted_time = scan_time.strftime("%H:%M:%S")
|
|
240
|
+
except:
|
|
241
|
+
formatted_time = scan_time_str
|
|
242
|
+
|
|
243
|
+
# Display scan summary for Python
|
|
244
|
+
click.echo("\n" + "=" * 60)
|
|
245
|
+
click.echo("Python Security Scan Results")
|
|
246
|
+
click.echo("=" * 60)
|
|
247
|
+
click.echo(f"Scan Time: {formatted_time}")
|
|
248
|
+
click.echo(f"Total Packages: {data.get('total_packages', 0)}")
|
|
249
|
+
click.echo(f"Vulnerable Packages: {data.get('vulnerable_packages', 0)}")
|
|
250
|
+
click.echo(f"Total Vulnerabilities: {data.get('total_vulnerabilities', 0)}")
|
|
251
|
+
click.echo("=" * 60)
|
|
252
|
+
|
|
253
|
+
# Display individual vulnerabilities
|
|
254
|
+
vulnerabilities = data.get("vulnerabilities", [])
|
|
255
|
+
if vulnerabilities:
|
|
256
|
+
click.echo("\nFound Python Vulnerabilities:\n")
|
|
257
|
+
for i, vuln in enumerate(vulnerabilities, 1):
|
|
258
|
+
click.echo(
|
|
259
|
+
f"{i}. Package: {click.style(vuln.get('package_name', 'Unknown'), fg='red', bold=True)}"
|
|
260
|
+
)
|
|
261
|
+
click.echo(
|
|
262
|
+
f" Installed Version: {vuln.get('installed_version', 'N/A')}"
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
severity = vuln.get("severity", "Unknown")
|
|
266
|
+
severity_color = {
|
|
267
|
+
"Critical": "red",
|
|
268
|
+
"High": "red",
|
|
269
|
+
"Medium": "yellow",
|
|
270
|
+
"Low": "green",
|
|
271
|
+
}.get(severity, "white")
|
|
272
|
+
click.echo(
|
|
273
|
+
f" Severity: {click.style(severity, fg=severity_color, bold=True)}"
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
fixed_versions = vuln.get("fixed_versions", [])
|
|
277
|
+
if fixed_versions:
|
|
278
|
+
click.echo(f" Fixed in: {', '.join(fixed_versions)}")
|
|
279
|
+
else:
|
|
280
|
+
click.echo(" Fixed in: No fix available yet")
|
|
281
|
+
click.echo()
|
|
282
|
+
|
|
283
|
+
except FileNotFoundError:
|
|
284
|
+
echo_warning("uv-secure not found. Install with: uv pip install uv-secure")
|
|
285
|
+
py_vulns = False
|
|
286
|
+
except json.JSONDecodeError as e:
|
|
287
|
+
echo_error(f"Error parsing uv-secure output: {e}")
|
|
288
|
+
echo_error(f"Raw output: {result.stdout[:500]}")
|
|
289
|
+
py_vulns = False
|
|
290
|
+
except Exception as e:
|
|
291
|
+
echo_error(f"Unexpected error during Python security check: {e}")
|
|
292
|
+
py_vulns = False
|
|
293
|
+
# npm vulnerability check
|
|
294
|
+
if shutil.which("npm"):
|
|
295
|
+
conf = config.get_profile("default")
|
|
296
|
+
builds = conf.get("builds", {})
|
|
297
|
+
sources_folder = conf.get("sources_folder", "./sources")
|
|
298
|
+
|
|
299
|
+
# Collect all npm builds
|
|
300
|
+
npm_builds = []
|
|
301
|
+
for build_name, build_config in builds.items():
|
|
302
|
+
if build_config.get("kind") == "npm":
|
|
303
|
+
source_path = build_config.get("source")
|
|
304
|
+
if source_path:
|
|
305
|
+
npm_builds.append(
|
|
306
|
+
{"name": build_name, "path": f"{sources_folder}/{source_path}"}
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
if not npm_builds:
|
|
310
|
+
click.echo("\n" + "=" * 60)
|
|
311
|
+
click.echo("No npm builds found in config - skipping npm audit")
|
|
312
|
+
click.echo("=" * 60)
|
|
313
|
+
else:
|
|
314
|
+
# Run audit for each npm build
|
|
315
|
+
for build in npm_builds:
|
|
316
|
+
try:
|
|
317
|
+
npm_audit_dir = build["path"]
|
|
318
|
+
|
|
319
|
+
npm_result = subprocess.run(
|
|
320
|
+
["npm", "audit", "--json"],
|
|
321
|
+
capture_output=True,
|
|
322
|
+
text=True,
|
|
323
|
+
cwd=npm_audit_dir,
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
npm_data = json.loads(npm_result.stdout, strict=False)
|
|
327
|
+
|
|
328
|
+
metadata = npm_data.get("metadata", {})
|
|
329
|
+
npm_vulnerabilities = npm_data.get("vulnerabilities", {})
|
|
330
|
+
|
|
331
|
+
vuln_counts = metadata.get("vulnerabilities", {})
|
|
332
|
+
dependencies = metadata.get("dependencies", {})
|
|
333
|
+
|
|
334
|
+
total_npm_vulns = vuln_counts.get("total", 0)
|
|
335
|
+
|
|
336
|
+
if total_npm_vulns > 0:
|
|
337
|
+
has_vulnerabilities = True
|
|
338
|
+
|
|
339
|
+
# Display npm scan summary
|
|
340
|
+
click.echo("\n" + "=" * 60)
|
|
341
|
+
click.echo(f"npm Security Scan Results - {build['name']}")
|
|
342
|
+
click.echo("=" * 60)
|
|
343
|
+
click.echo(f"Build Path: {npm_audit_dir}")
|
|
344
|
+
click.echo(
|
|
345
|
+
f"Total Dependencies: {dependencies.get('total', 0)}"
|
|
346
|
+
)
|
|
347
|
+
click.echo(f"Total Vulnerabilities: {total_npm_vulns}")
|
|
348
|
+
click.echo(
|
|
349
|
+
f" Critical: {vuln_counts.get('critical', 0)}"
|
|
350
|
+
)
|
|
351
|
+
click.echo(f" High: {vuln_counts.get('high', 0)}")
|
|
352
|
+
click.echo(
|
|
353
|
+
f" Moderate: {vuln_counts.get('moderate', 0)}"
|
|
354
|
+
)
|
|
355
|
+
click.echo(f" Low: {vuln_counts.get('low', 0)}")
|
|
356
|
+
click.echo(f" Info: {vuln_counts.get('info', 0)}")
|
|
357
|
+
click.echo("=" * 60)
|
|
358
|
+
|
|
359
|
+
# Display individual npm vulnerabilities
|
|
360
|
+
if npm_vulnerabilities and total_npm_vulns > 0:
|
|
361
|
+
click.echo(f"\nFound npm Vulnerabilities in {build['name']}:\n")
|
|
362
|
+
for i, (pkg_name, vuln_info) in enumerate(
|
|
363
|
+
npm_vulnerabilities.items(), 1
|
|
364
|
+
):
|
|
365
|
+
severity = vuln_info.get("severity", "unknown")
|
|
366
|
+
severity_color = {
|
|
367
|
+
"critical": "red",
|
|
368
|
+
"high": "red",
|
|
369
|
+
"moderate": "yellow",
|
|
370
|
+
"low": "green",
|
|
371
|
+
"info": "blue",
|
|
372
|
+
}.get(severity.lower(), "white")
|
|
373
|
+
|
|
374
|
+
# Get current version from nodes
|
|
375
|
+
nodes = vuln_info.get("nodes", [])
|
|
376
|
+
current_version = vuln_info.get("range", "N/A")
|
|
377
|
+
|
|
378
|
+
click.echo(
|
|
379
|
+
f"{i}. Package: {click.style(pkg_name, fg='red', bold=True)}"
|
|
380
|
+
)
|
|
381
|
+
click.echo(f" Current Range: {current_version}")
|
|
382
|
+
click.echo(
|
|
383
|
+
f" Severity: {click.style(severity.capitalize(), fg=severity_color, bold=True)}"
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
# Check for breaking changes
|
|
387
|
+
fix_available = vuln_info.get("fixAvailable", {})
|
|
388
|
+
if fix_available:
|
|
389
|
+
fix_package = fix_available.get("name", pkg_name)
|
|
390
|
+
fix_version = fix_available.get("version", "N/A")
|
|
391
|
+
is_breaking = fix_available.get("isSemVerMajor", False)
|
|
392
|
+
|
|
393
|
+
fix_text = f" Fixed in: {fix_package}@{fix_version}"
|
|
394
|
+
if is_breaking:
|
|
395
|
+
fix_text += click.style(
|
|
396
|
+
" (breaking change)", fg="yellow", bold=True
|
|
397
|
+
)
|
|
398
|
+
click.echo(fix_text)
|
|
399
|
+
else:
|
|
400
|
+
click.echo(" Fixed in: No fix available yet")
|
|
401
|
+
|
|
402
|
+
click.echo()
|
|
403
|
+
|
|
404
|
+
except FileNotFoundError:
|
|
405
|
+
echo_warning(f"npm audit directory not found: {build['path']}")
|
|
406
|
+
except json.JSONDecodeError as e:
|
|
407
|
+
echo_error(
|
|
408
|
+
f"Error parsing npm audit output for {build['name']}: {e}"
|
|
409
|
+
)
|
|
410
|
+
except Exception as e:
|
|
411
|
+
echo_error(
|
|
412
|
+
f"Unexpected error during npm security check for {build['name']}: {e}"
|
|
413
|
+
)
|
|
414
|
+
else:
|
|
415
|
+
click.echo("\n" + "=" * 60)
|
|
416
|
+
click.echo("npm not found - skipping npm audit")
|
|
417
|
+
click.echo("=" * 60)
|
|
418
|
+
|
|
419
|
+
return not has_vulnerabilities
|
|
@@ -12,7 +12,6 @@ from weakref import proxy
|
|
|
12
12
|
from viur.scriptor import Modules
|
|
13
13
|
from ..cli import cli
|
|
14
14
|
from ..cli import scriptor_config
|
|
15
|
-
from .login import ensure_login
|
|
16
15
|
|
|
17
16
|
# Global modules instance that will be initialized when needed
|
|
18
17
|
_modules = None
|
|
@@ -44,6 +43,7 @@ def configure(url: str, username: str, working_dir: str):
|
|
|
44
43
|
Manage configuration settings.
|
|
45
44
|
"""
|
|
46
45
|
|
|
46
|
+
|
|
47
47
|
if url:
|
|
48
48
|
scriptor_config["base_url"] = url
|
|
49
49
|
|
|
@@ -61,14 +61,6 @@ def setup():
|
|
|
61
61
|
"""
|
|
62
62
|
|
|
63
63
|
base_url = scriptor_config.get("base_url")
|
|
64
|
-
|
|
65
|
-
try:
|
|
66
|
-
click.echo("This Feature is only avalible on viur-core 3.8.19 or higher.")
|
|
67
|
-
res = ensure_login("", host=base_url)
|
|
68
|
-
if res:
|
|
69
|
-
return
|
|
70
|
-
except KeyboardInterrupt:
|
|
71
|
-
pass
|
|
72
64
|
try:
|
|
73
65
|
session = requests.session()
|
|
74
66
|
skey = session.get(base_url + "/json/skey")
|
|
@@ -104,11 +96,15 @@ def check_session(ctx: click.Context):
|
|
|
104
96
|
|
|
105
97
|
response = s.get(base_url + "/vi/user/view/self", cookies=scriptor_config.get("cookies", {}))
|
|
106
98
|
if not response.ok:
|
|
107
|
-
click.echo("Invalid session, please run `viur script setup` again.
|
|
99
|
+
click.echo("Invalid session, please run `viur script setup` again.")
|
|
108
100
|
ctx.invoke(setup)
|
|
109
101
|
ctx.close()
|
|
102
|
+
#FIXME We need this ?
|
|
103
|
+
# Update modules with cookies
|
|
104
|
+
modules = get_modules()
|
|
105
|
+
# modules.request.cookies = cookiejar_from_dict(scriptor_config.get("cookies", {}))
|
|
106
|
+
|
|
110
107
|
|
|
111
|
-
|
|
112
108
|
@script.command()
|
|
113
109
|
@click.option('--force', default=False, help='Force replace files from server in local working directory')
|
|
114
110
|
@click.pass_context
|
|
@@ -118,6 +114,7 @@ def pull(ctx: click.Context, force: bool):
|
|
|
118
114
|
"""
|
|
119
115
|
check_session(ctx)
|
|
120
116
|
|
|
117
|
+
|
|
121
118
|
async def main():
|
|
122
119
|
# In the new API, we don't need to call structure
|
|
123
120
|
modules = get_modules()
|
|
@@ -291,7 +288,7 @@ def push(ctx: click.Context, force: bool, watch: bool):
|
|
|
291
288
|
f.write(args["script"])
|
|
292
289
|
|
|
293
290
|
click.echo(f"Push {_real_file}")
|
|
294
|
-
await tree.add(
|
|
291
|
+
await tree.add(_type, args)
|
|
295
292
|
|
|
296
293
|
if watch:
|
|
297
294
|
print("Watching...")
|