Skip to content

Harden error output, spool delivery, report files and catalogue loading - #111

Merged
bencehezso merged 2 commits into
release/2.0from
task/cli2_security_fixes
Oct 25, 2026
Merged

bencehezso merged 2 commits into
release/2.0from
task/cli2_security_fixes

Conversation

@bencehezso

Copy link
Copy Markdown
Contributor

Summary

Six small fixes to how the CLI handles untrusted text, unexpected platform answers, report folders, spooled syncs, catalogue-supplied paths and catalogue files. Each has a test, and nothing else changes.

Changes

Text from the platform and from exceptions is shown literally

  • Step details, error lines and exception messages are printed with Rich markup and emoji codes off, so text such as [/bold] can no longer raise MarkupError. A platform error now ends in its mapped exit code with the reports still written, instead of exit 1 with no reports.

Unexpected platform answers no longer crash status or --output json

  • Every field read from /meta, /whoami and the sync answer is checked for its type, and a wrong type counts as unknown. An expiry no calendar holds shows as "unknown".
  • --output json always writes exactly one JSON object to stdout. If the summary cannot be built it carries the exit code and an error.

Runs never share a report folder

  • The folder is created exclusively as YYYYMMDDHHMMSS-xxxxxxxx, so two runs in the same second get different folders.

Spooled syncs are delivered only with the key that spooled them

  • Each spooled sync records a fingerprint of the API key in use (a hash, never the key). Flush and the automatic flush deliver only the syncs for the key in use, leave the others where they are, say so, and spool list marks them. A spooled sync with no recorded key is treated as another key's.
  • README: a re-run delivers the spooled sync under its own sync ID and then makes a new assessment, which is a new sync and is charged.

Report files are private, and catalogue paths stay in the assets folder

  • The report folder is created 0700 and sync.json is written 0600. When a run ends, everything under the report folder loses its group and other permissions; stricter modes are kept.
  • Icon paths from the catalogue go through one check used by both lookups: no .., backslashes, UNC, drive letters, : or NUL, and the resolved path must stay inside the assets folder. A path that fails gets the placeholder, and nothing outside the folder is looked at.

Catalogue loading is stricter

  • A catalogue holding a trigger or a view is refused at install, with --catalogue, and again on the copy made into the report folder. The stage then fails before any collected data is written.
  • Connections set trusted_schema=OFF.
  • A ./datasets/data.db is no longer installed automatically; use cloudexit catalogue import.

Compatibility

  • Config, flags, the sync document, report content, exit-code meanings and docs/api-v2/: unchanged.
  • Visible changes: untrusted text shown literally; report folder names gain a suffix; --output json always prints one object; odd platform answers give degraded output instead of tracebacks; mismatched spooled syncs are not delivered; catalogues with triggers or views are refused; report files are readable by the owner only; ./datasets/data.db is no longer picked up on its own.
  • Developer-facing: cloudexit.private_files is a new module, with its layering entry added.

@bencehezso bencehezso self-assigned this Oct 25, 2026
@bencehezso bencehezso added the enhancement New feature or request label Oct 25, 2026
@bencehezso
bencehezso merged commit 00a5de0 into release/2.0 Oct 25, 2026
11 checks passed
@bencehezso
bencehezso deleted the task/cli2_security_fixes branch October 25, 2026 17:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant