Skip to content

fix(telemetry): create ~/.nansen 0700 and its id/session files 0600 - #582

Open
devorun wants to merge 1 commit into
nansen-ai:mainfrom
devorun:fix/telemetry-config-dir-permissions
Open

fix(telemetry): create ~/.nansen 0700 and its id/session files 0600#582
devorun wants to merge 1 commit into
nansen-ai:mainfrom
devorun:fix/telemetry-config-dir-permissions

Conversation

@devorun

@devorun devorun commented Sep 4, 2026

Copy link
Copy Markdown

Problem

~/.nansen holds the API key (config.json) and the encrypted wallet keystores (wallets/). Every module that writes there creates directories with mode: 0o700 and files with mode: 0o600api.js, wallet.js, keychain.js, privy.js, trading.js, bridge.js, limit-order.js, update-check.js, cost-cache.js, commands/mcp.js.

telemetry.js is the only exception. Its two mkdirSync calls and three writeFileSync calls use default permissions.

That matters because mkdirSync(..., { recursive: true }) does not change the mode of a directory that already exists — whichever module creates ~/.nansen first sets its permissions permanently. Telemetry runs on every non-offline command, so it wins that race whenever no config has been saved yet: most importantly on the documented NANSEN_API_KEY env-var path (AGENTS.md, and what the MCP/agent setup uses), where saveConfig() never runs at all.

Reproduced on Linux with a clean HOME, calling only getAnonymousId() and getSessionId():

umask            : 0022
~/.nansen        : 755
telemetry-id     : 644
session          : 644
after 0o700 mkdir: 755   <- a later mkdir(mode 0o700, recursive) does not fix it

nansen doctor classifies exactly this as insecure — isInsecureMode() in src/doctor.js is (mode & 0o077) !== 0, and it tells the user to chmod 700. So the CLI ends up warning about a state it created itself.

Fix

Give telemetry's directory and file writes the same modes the rest of the codebase already uses: 0o700 for the directory, 0o600 for telemetry-id and session.

No behaviour change beyond permissions. The two id files stay exactly where they were, and mode is ignored for an already-existing file, so upgrading users are unaffected until the files are recreated.

Tests

Two regression tests in src/__tests__/telemetry.test.js:

  • a mock-based one asserting the options passed to mkdirSync/writeFileSync (runs everywhere);
  • a real-filesystem one against a temp HOME, asserting nothing is group- or world-accessible using the same mode & 0o077 predicate doctor.js uses. Skipped on Windows, matching the existing platform !== 'win32' guard in doctor.js.

Both fail on main and pass with this change.

Checklist

  • npm test passes
  • npm run lint passes
  • New code paths have tests
  • No console.log in core, no hardcoded secrets
  • Changeset added (patch)
  • src/schema.json unchanged — no commands or options added

npm test:

 Test Files  64 passed (64)
      Tests  2637 passed | 2 skipped (2639)

@nansen-pr-reviewer

Copy link
Copy Markdown

pr-reviewer Summary for #8e674c2

No issues found

The code review completed successfully with no findings.

Review effort: 1/5 (Trivial)

Summary

This PR correctly fixes a real security bug: telemetry.js was the only module writing under ~/.nansen without explicit permission modes, meaning it could create the shared config directory with loose permissions (world/group readable) when it ran first — exactly the condition nansen doctor flags as insecure.

The fix is minimal and correct: two named constants (DIR_MODE = 0o700, FILE_MODE = 0o600) are introduced with a clear comment explaining why, and all five mkdirSync/writeFileSync calls are updated to use them, matching the existing pattern in api.js, wallet.js, and every other module in the codebase. All three code paths are covered — initial ID creation, initial session creation, and the "touch" session timestamp path (line 131, which reuses the same FILE_MODE constant).

The tests are well-structured: a fast mock-based assertion on the options passed to fs.*Sync, and a real-filesystem integration test (skipped on Windows, consistent with doctor.js) using the same mode & 0o077 predicate that doctor.js itself uses. The changeset is present with the correct patch bump and an accurate description.


Token usage: 12 input, 2,020 output, 144,441 cache read, 28,782 cache write | Usage Guide

New pushes are reviewed automatically with a 10-minute cooldown between reviews. To request a review at any time, comment @nansen-pr-reviewer re-review.

@nansen-pr-reviewer nansen-pr-reviewer 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.

Auto-approved

This PR was automatically approved because:

  • Claude recommends approval
  • Claude assessed this as a minimal effort change
  • The effort level is within the auto-approval threshold of 2
  • No high or critical issues were detected

If you have any concerns, please request a manual review.

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