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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: obvyr-cli
3
- Version: 2.7.0
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.0"
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/{project_config['version']}",
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
- with open(output_path, "wb") as output_file:
292
- output_file.write(compressed_data)
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
 
@@ -464,5 +464,7 @@ def cli_run_process(
464
464
  if response.returncode != 0:
465
465
  raise click.exceptions.Exit(response.returncode)
466
466
 
467
+ except click.exceptions.Exit:
468
+ raise
467
469
  except Exception as e:
468
470
  raise click.ClickException(str(e)) from e
@@ -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."""
@@ -0,0 +1,6 @@
1
+ from importlib.metadata import version
2
+
3
+
4
+ def get_version() -> str:
5
+ """Return the installed CLI version from package metadata."""
6
+ return version("obvyr-cli")
@@ -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