obvyr-cli 2.7.0__tar.gz → 2.8.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.
- {obvyr_cli-2.7.0 → obvyr_cli-2.8.0}/PKG-INFO +1 -2
- {obvyr_cli-2.7.0 → obvyr_cli-2.8.0}/pyproject.toml +1 -3
- {obvyr_cli-2.7.0 → obvyr_cli-2.8.0}/src/obvyr_cli/api_client.py +1 -3
- {obvyr_cli-2.7.0 → obvyr_cli-2.8.0}/src/obvyr_cli/archive_builder.py +12 -8
- {obvyr_cli-2.7.0 → obvyr_cli-2.8.0}/src/obvyr_cli/cli.py +2 -0
- {obvyr_cli-2.7.0 → obvyr_cli-2.8.0}/src/obvyr_cli/config.py +13 -0
- obvyr_cli-2.8.0/src/obvyr_cli/utils.py +6 -0
- obvyr_cli-2.7.0/src/obvyr_cli/utils.py +0 -13
- {obvyr_cli-2.7.0 → obvyr_cli-2.8.0}/README_PYPI.md +0 -0
- {obvyr_cli-2.7.0 → obvyr_cli-2.8.0}/src/obvyr_cli/__init__.py +0 -0
- {obvyr_cli-2.7.0 → obvyr_cli-2.8.0}/src/obvyr_cli/command_wrapper.py +0 -0
- {obvyr_cli-2.7.0 → obvyr_cli-2.8.0}/src/obvyr_cli/error_handling.py +0 -0
- {obvyr_cli-2.7.0 → obvyr_cli-2.8.0}/src/obvyr_cli/jwt_utils.py +0 -0
- {obvyr_cli-2.7.0 → obvyr_cli-2.8.0}/src/obvyr_cli/logging_config.py +0 -0
- {obvyr_cli-2.7.0 → obvyr_cli-2.8.0}/src/obvyr_cli/redaction.py +0 -0
- {obvyr_cli-2.7.0 → obvyr_cli-2.8.0}/src/obvyr_cli/schemas.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: obvyr-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.8.0
|
|
4
4
|
Summary: Command-line wrapper to allow Obvyr to capture evidence from any machine.
|
|
5
5
|
License: Proprietary
|
|
6
6
|
Author: Wyrd Technology Ltd
|
|
@@ -16,7 +16,6 @@ Requires-Dist: pydantic (==2.13.5)
|
|
|
16
16
|
Requires-Dist: pydantic-settings (==2.15.0)
|
|
17
17
|
Requires-Dist: python-dotenv (==1.2.4)
|
|
18
18
|
Requires-Dist: tenacity (==9.1.4)
|
|
19
|
-
Requires-Dist: toml (==0.10.2)
|
|
20
19
|
Requires-Dist: zstandard (==0.25.0)
|
|
21
20
|
Project-URL: Homepage, https://wyrd-technology.com
|
|
22
21
|
Description-Content-Type: text/markdown
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "obvyr-cli"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.8.0"
|
|
8
8
|
description = "Command-line wrapper to allow Obvyr to capture evidence from any machine."
|
|
9
9
|
authors = ["Wyrd Technology Ltd"]
|
|
10
10
|
license = "Proprietary"
|
|
@@ -34,7 +34,6 @@ pydantic = "2.13.5"
|
|
|
34
34
|
pydantic-settings = "2.15.0"
|
|
35
35
|
httpx = "0.28.1"
|
|
36
36
|
tenacity = "9.1.4"
|
|
37
|
-
toml = "0.10.2"
|
|
38
37
|
orjson = "3.12.0"
|
|
39
38
|
zstandard = "0.25.0"
|
|
40
39
|
|
|
@@ -46,7 +45,6 @@ pytest-cov = "7.1.0"
|
|
|
46
45
|
black = "26.5.1"
|
|
47
46
|
pyproject-fmt = "2.29.4"
|
|
48
47
|
pytest-subprocess = "1.6.0"
|
|
49
|
-
types-toml = "^0.10.8.20240310"
|
|
50
48
|
poethepoet = "0.48.0"
|
|
51
49
|
|
|
52
50
|
[tool.poetry.scripts]
|
|
@@ -16,8 +16,6 @@ from obvyr_cli.logging_config import get_logger
|
|
|
16
16
|
|
|
17
17
|
logger = get_logger("api_client")
|
|
18
18
|
|
|
19
|
-
project_config = utils.get_project_config()
|
|
20
|
-
|
|
21
19
|
|
|
22
20
|
class ObvyrAPIClient:
|
|
23
21
|
"""Client for sending execution data to the Obvyr API."""
|
|
@@ -43,7 +41,7 @@ class ObvyrAPIClient:
|
|
|
43
41
|
"""Generate headers for authentication."""
|
|
44
42
|
return {
|
|
45
43
|
"Authorization": (f"Bearer {self.api_key}"),
|
|
46
|
-
"User-Agent": f"obvyr-cli/{
|
|
44
|
+
"User-Agent": f"obvyr-cli/{utils.get_version()}",
|
|
47
45
|
}
|
|
48
46
|
|
|
49
47
|
@retry(
|
|
@@ -9,6 +9,7 @@ import io
|
|
|
9
9
|
import json
|
|
10
10
|
import logging
|
|
11
11
|
import mimetypes
|
|
12
|
+
import os
|
|
12
13
|
import pathlib
|
|
13
14
|
import tarfile
|
|
14
15
|
import tempfile
|
|
@@ -227,9 +228,6 @@ def build_artifacts_tar_zst(
|
|
|
227
228
|
Invalid or oversized attachments are skipped with warning logs.
|
|
228
229
|
Attachments are prioritized by value (XML/JSON > YAML/CSV > TXT/LOG).
|
|
229
230
|
"""
|
|
230
|
-
if tmp_dir is None:
|
|
231
|
-
tmp_dir = pathlib.Path(tempfile.gettempdir())
|
|
232
|
-
|
|
233
231
|
# Select valid attachments with priority-based ordering
|
|
234
232
|
selected_attachments: list[pathlib.Path] = []
|
|
235
233
|
if attachment_paths:
|
|
@@ -237,9 +235,6 @@ def build_artifacts_tar_zst(
|
|
|
237
235
|
attachment_paths
|
|
238
236
|
)
|
|
239
237
|
|
|
240
|
-
# Create output file path
|
|
241
|
-
output_path = tmp_dir / "artifacts.tar.zst"
|
|
242
|
-
|
|
243
238
|
# Prepare command.json data exactly as specified in the doc
|
|
244
239
|
command_data = {
|
|
245
240
|
"command": run_command_response.command,
|
|
@@ -288,8 +283,17 @@ def build_artifacts_tar_zst(
|
|
|
288
283
|
compressor = zstd.ZstdCompressor(write_content_size=True)
|
|
289
284
|
compressed_data = compressor.compress(tar_data)
|
|
290
285
|
|
|
291
|
-
|
|
292
|
-
|
|
286
|
+
# mkstemp creates a unique file readable and writable by the owner only
|
|
287
|
+
output_fd, output_name = tempfile.mkstemp(
|
|
288
|
+
prefix="obvyr-", suffix=".tar.zst", dir=tmp_dir
|
|
289
|
+
)
|
|
290
|
+
output_path = pathlib.Path(output_name)
|
|
291
|
+
try:
|
|
292
|
+
with os.fdopen(output_fd, "wb") as output_file:
|
|
293
|
+
output_file.write(compressed_data)
|
|
294
|
+
except Exception:
|
|
295
|
+
output_path.unlink(missing_ok=True)
|
|
296
|
+
raise
|
|
293
297
|
|
|
294
298
|
return output_path
|
|
295
299
|
|
|
@@ -9,6 +9,7 @@ import logging
|
|
|
9
9
|
import re
|
|
10
10
|
import secrets
|
|
11
11
|
from typing import Dict, Literal
|
|
12
|
+
from urllib.parse import urlparse
|
|
12
13
|
|
|
13
14
|
from dotenv import load_dotenv
|
|
14
15
|
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
@@ -29,6 +30,7 @@ class ProfileSettings(BaseModel):
|
|
|
29
30
|
ATTACHMENT_MAX_AGE_SECONDS: int = 10
|
|
30
31
|
TIMEOUT: float = 10.0
|
|
31
32
|
VERIFY_SSL: bool = True
|
|
33
|
+
ALLOW_INSECURE_API_URL: bool = False
|
|
32
34
|
TAGS: list[str] = Field(default_factory=list)
|
|
33
35
|
ENV_MODE: Literal["allowlist", "none"] = "allowlist"
|
|
34
36
|
ENV_INCLUDE: list[str] = Field(default_factory=list)
|
|
@@ -61,6 +63,17 @@ class ProfileSettings(BaseModel):
|
|
|
61
63
|
) from error
|
|
62
64
|
return value
|
|
63
65
|
|
|
66
|
+
@model_validator(mode="after")
|
|
67
|
+
def require_https_api_url(self) -> "ProfileSettings":
|
|
68
|
+
"""Fail fast on an API_URL that would send the token and archive in clear text."""
|
|
69
|
+
is_https = urlparse(self.API_URL).scheme.lower() == "https"
|
|
70
|
+
if self.API_URL and not is_https and not self.ALLOW_INSECURE_API_URL:
|
|
71
|
+
raise ValueError(
|
|
72
|
+
f"API_URL '{self.API_URL}' must use HTTPS. "
|
|
73
|
+
"Set ALLOW_INSECURE_API_URL to true to override."
|
|
74
|
+
)
|
|
75
|
+
return self
|
|
76
|
+
|
|
64
77
|
@model_validator(mode="after")
|
|
65
78
|
def migrate_attachment_path(self) -> "ProfileSettings":
|
|
66
79
|
"""Migrate old ATTACHMENT_PATH to ATTACHMENT_PATHS."""
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
from importlib.metadata import version
|
|
2
|
-
|
|
3
|
-
import toml
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def get_project_config(pyproject_path: str = "pyproject.toml") -> dict:
|
|
7
|
-
try:
|
|
8
|
-
with open(pyproject_path, "r") as file:
|
|
9
|
-
config = toml.load(file)
|
|
10
|
-
return config["tool"]["poetry"]
|
|
11
|
-
except (KeyError, FileNotFoundError):
|
|
12
|
-
# Fall back to package metadata for installed versions
|
|
13
|
-
return {"version": version("obvyr-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
|