Skip to content

[security] fix(daemon): keep daemon config private#214

Merged
steipete merged 2 commits into
steipete:mainfrom
Hinotoi-agent:fix/daemon-config-private-permissions
May 8, 2026
Merged

[security] fix(daemon): keep daemon config private#214
steipete merged 2 commits into
steipete:mainfrom
Hinotoi-agent:fix/daemon-config-private-permissions

Conversation

@Hinotoi-agent
Copy link
Copy Markdown
Contributor

Summary

This PR hardens daemon credential storage by ensuring the generated daemon config directory and config file are private on POSIX filesystems.

The daemon config can contain bearer tokens used by the local daemon and an environment snapshot with provider credentials such as API keys. This change makes the default write path secure-by-default and repairs previously loose permissions when the config is rewritten.

Security issues covered

Issue Impact Severity
Daemon config file created with default filesystem permissions Another local account on the same machine could read daemon bearer tokens and persisted provider/API environment values from ~/.summarize/daemon.json Medium

Before this PR

  • writeDaemonConfig(...) created ~/.summarize with default directory permissions.
  • daemon.json was written with default file creation permissions.
  • On common Unix-like defaults, this can create a world-readable file depending on the process umask.
  • Existing loose directory/file permissions were not repaired when the config was rewritten.

After this PR

  • ~/.summarize is created with 0700 permissions.
  • daemon.json is written with 0600 permissions.
  • Existing loose permissions are best-effort repaired with chmod after writing.
  • POSIX permission behavior is covered by a regression test that verifies 0755 / 0644 paths are tightened to 0700 / 0600.

Why this matters

The daemon config is a local credential boundary. It can contain:

  • daemon bearer token material used for /v1/* daemon authorization
  • provider/API-related environment values captured for daemon execution

If this file is readable by another local account, that account can recover secrets that were intended to stay within the owner account. The daemon token may also allow local requests against the victim's daemon while it is running.

Attack flow

shared Unix-like machine
    -> daemon config created with default permissions
        -> another local user reads ~/.summarize/daemon.json
            -> daemon bearer token and captured API/provider env values are disclosed

Affected code

Issue Files
Private daemon config storage src/daemon/config.ts, tests/daemon.config.test.ts

Root cause

Daemon config file created with default filesystem permissions:

  • The write path used fs.mkdir(..., { recursive: true }) and fs.writeFile(..., "utf8") without explicit private modes.
  • The daemon config contains credential material, but the filesystem boundary did not enforce owner-only access.

CVSS assessment

Issue CVSS v3.1 Vector
Local daemon config credential disclosure 6.1 Medium CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N

Rationale:

  • Exploitation requires local access as another account on the same system.
  • No user interaction is required after the victim has written the daemon config.
  • Confidentiality impact is high because daemon tokens and provider/API environment values may be exposed.
  • Integrity impact is low because a stolen daemon token may allow local daemon requests while the daemon is active.

Safe reproduction steps

  1. On a Unix-like system, create a loose config directory and file:

    mkdir -p ~/.summarize
    chmod 0755 ~/.summarize
    printf '{}\n' > ~/.summarize/daemon.json
    chmod 0644 ~/.summarize/daemon.json
  2. Trigger a daemon config write through the existing config-writing path.

  3. On vulnerable code, observe that the config path can remain readable beyond the owning user depending on umask/existing permissions.

  4. With this PR, run the regression test and observe that both permissions are tightened.

Expected vulnerable behavior

  • ~/.summarize/daemon.json could be created or left with non-private permissions.
  • Sensitive daemon token and environment snapshot values could be readable by another local user.

Changes in this PR

  • Creates the daemon config directory with 0o700.
  • Best-effort repairs the daemon config directory to 0o700.
  • Writes daemon.json with 0o600.
  • Best-effort repairs the daemon config file to 0o600 after writing.
  • Adds a POSIX regression test for existing loose directory/file permissions.

Files changed

Category Files What changed
Daemon config hardening src/daemon/config.ts Adds private directory/file modes and best-effort chmod repair after writes
Regression tests tests/daemon.config.test.ts Verifies loose POSIX config permissions are tightened by writeDaemonConfig(...)

Maintainer impact

  • This is a narrow daemon config storage hardening change.
  • The config format, token generation, daemon routes, and client behavior are unchanged.
  • chmod failures are ignored intentionally so Windows and filesystems without POSIX mode support continue to work.
  • Existing installations with loose permissions are repaired the next time the daemon config is written.

Fix rationale

The daemon config stores secrets, so the durable boundary should be the filesystem owner account. Setting private creation modes and repairing existing loose permissions is a small, durable defense that matches how local credential files are typically handled.

Type of change

  • Security fix
  • Tests
  • Documentation update
  • Refactor with no behavior change

Test plan

  • pnpm install --frozen-lockfile
  • pnpm -s format:check src/daemon/config.ts tests/daemon.config.test.ts
  • pnpm -s lint src/daemon/config.ts tests/daemon.config.test.ts
  • pnpm exec vitest run tests/daemon.config.test.ts
  • pnpm -C packages/core -s build
  • pnpm -s typecheck
  • pnpm -s test
  • git diff --check

Note: local validation ran under Node v22.22.2; the repository declares node >=24, so pnpm emitted engine warnings, but the commands above completed successfully.

Disclosure notes

  • This PR is intentionally bounded to local daemon config file permissions.
  • It does not claim a remote daemon bypass.
  • I did not find a SECURITY.md file in the checkout.
  • No unrelated files are changed.

Copy link
Copy Markdown
Owner

@steipete steipete left a comment

Choose a reason for hiding this comment

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

Maintainer fixup pushed in 7f90d26: kept the private directory/file modes from the PR, added a pre-write chmod so existing loose daemon configs are tightened before rewriting token/env secrets, and added changelog coverage.

Verified locally: pnpm -s test tests/daemon.config.test.ts; pnpm -s check; pnpm -s build. Remote CI is green on 7f90d26, including test/build/pack and extension-e2e.

@steipete steipete merged commit 0cfb0fb into steipete:main May 8, 2026
3 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.

2 participants