Skip to content

fix(cli): refuse non-object settings; only auto-tighten the default ccxray home - #702

Merged
lis186 merged 6 commits into
mainfrom
fix/695-followup
Oct 20, 2026
Merged

lis186 merged 6 commits into
mainfrom
fix/695-followup

Conversation

@lis186

@lis186 lis186 commented Oct 17, 2026

Copy link
Copy Markdown
Owner

Summary

Follow-ups to #695 from its security review.

  • setup-statusline refuses non-object settings. A settings.json that parses to null, an array, a number, a string or a boolean is refused like unparseable JSON (same one-line message, no write, no stack trace) on install, --remove and the launch prompt. The launch prompt now uses the same reader as setup instead of re-parsing, so it refuses before asking. A non-object statusLine is no longer spread into character keys, and a non-string command is not reported as invalid JSON.
  • Only the default ccxray home is auto-tightened. tightenCcxrayHome chmods 0700 the default ~/.ccxray, or a custom CCXRAY_HOME that already holds a ccxray-unique marker (local-secret or hub.sock, as a regular file/socket owned by the current user, symlinks ignored). Any other custom home gets a one-line warning and keeps its mode, so a shared dir, mount or $HOME is never locked to 0700.

Verification

  • Unit group (2 workers): 2069/2069 pass; home-permissions, statusline-setup, setup tests: 129 pass, 4 skipped, 0 fail.
  • Independent security review of the first commit (fable): no blocker; its two should-fix items (launch-prompt path, marker spoofing) are addressed in the second commit.

lis186 and others added 2 commits October 17, 2026 22:18
…t ccxray home

- setup-statusline: a settings.json that parses to null, an array, a number
  or a string is refused like unparseable JSON (same one-line message, no
  write, no stack trace); planUninstall and isStatuslineInstalled share the
  guard. A non-object statusLine is no longer spread into character keys.
- tightenCcxrayHome chmods 0700 only the default ~/.ccxray or a custom
  CCXRAY_HOME that already holds ccxray files (local-secret, hub.json,
  hub.log); any other custom home gets a one-line warning and keeps its mode.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-17T14:21:38.251652Z 0bccbc4 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0bccbc45cb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread server/paths.js Outdated
Comment on lines +69 to +72
if (!isDefault(home) && !holdsCcxrayFiles(home)) {
if (log) log(`[ccxray] warning: CCXRAY_HOME ${home} is group/world-accessible (${fmt(st.mode & 0o7777)}); ccxray stores secrets and logs there — run chmod 700 on it if it is not shared`);
return 'warned';
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not infer custom-home ownership from generated markers

When CCXRAY_HOME is a shared directory, the first run warns and keeps its mode, but auth.readOrCreateEphemeralSecret() then creates local-secret there. On every subsequent invocation this condition treats that self-created file as proof that the entire directory belongs to ccxray and executes chmod 0700, locking other users out of the shared directory—the exact custom-home scenario this change is intended to preserve. Custom homes should continue to warn rather than become eligible for chmod merely because ccxray has used them once.

Useful? React with 👍 / 👎.

lis186 and others added 4 commits October 18, 2026 01:49
…ust be an object

- tightenCcxrayHome chmods only the default ~/.ccxray. The local-secret /
  hub.sock marker exception is gone: ccxray writes local-secret into
  whatever home it is given, so on the second run a shared dir (or $HOME)
  became chmod-eligible. A loose custom home now warns on every startup and
  keeps its mode, and $HOME or any ancestor is refused outright.
- setup-statusline / launch prompt / --dry-run: null, [] and other
  non-object JSON report "must contain a JSON object" instead of
  "is not valid JSON".
- Tests: a pre-existing 0755 custom home stays 0755 after ccxray creates
  its secret and runs again; CCXRAY_HOME=$HOME is never chmod'ed; the
  default home still tightens; a PTY launch with non-object settings
  refuses without asking, rewriting, or writing the declined marker
  (fails when the launchPromptState refusal is removed from index.js).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
tightenCcxrayHome compared lexical paths while statSync/chmodSync follow
symlinks, so `~/.ccxray -> <shared 0755 dir>` or `~/.ccxray -> .` let
startup chmod the target (including $HOME) to 0700.

- lstat the home first; a symlinked default home is warning-only
- refuse $HOME/ancestors by canonical (realpath) as well as lexical path,
  and require realpath(home) === realpath(homedir)/.ccxray to tighten
- chmod via an O_NOFOLLOW|O_DIRECTORY descriptor whose dev/ino must match
  the lstat result, closing the check-then-chmod symlink swap window

Regression tests: symlink to a shared dir, `.ccxray -> .` (unit + startup);
all three fail on the previous paths.js.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@lis186
lis186 merged commit bf2ff86 into main Oct 20, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant