Skip to content

fix(security): restrict tee file/directory permissions to 0600/0700#656

Open
rjamestaylor wants to merge 3 commits intortk-ai:developfrom
rjamestaylor:fix/tee-file-permissions
Open

fix(security): restrict tee file/directory permissions to 0600/0700#656
rjamestaylor wants to merge 3 commits intortk-ai:developfrom
rjamestaylor:fix/tee-file-permissions

Conversation

@rjamestaylor
Copy link

Summary

Tee files written to ~/.local/share/rtk/tee/ can contain sensitive command output — error messages often include tokens, API keys, or credentials. Previously these files were created with the process umask (typically 0644), making them readable by other users on the same system.

Changes

  • Tee directory: chmod 0700 (owner rwx only) applied after create_dir_all
  • Tee files: chmod 0600 (owner rw only) applied after each fs::write
  • Both changes guarded by #[cfg(unix)] — Windows behaviour is unchanged
  • Hint line updated: [full output: <path> — may contain sensitive data]
  • New test test_write_tee_file_permissions (#[cfg(unix)]) verifies both modes

Files changed

  • src/tee.rs: permissions logic + updated hint text + new test
  • CHANGELOG.md: unreleased entry

Test plan

  • cargo fmt --all --check — clean
  • cargo clippy --all-targets — zero warnings
  • cargo test --all — 935 tests pass (includes new permission test)
  • Manual: tee file created on macOS, ls -l confirms -rw------- and drwx------

Checklist

  • Single scope (tee permissions only)
  • CHANGELOG.md updated
  • All tests pass
  • DCO Signed-off-by present
  • Targets develop branch

🤖 Generated with Claude Code

pszymkowiak and others added 3 commits March 16, 2026 14:58
* fix: P1 exit codes, grep regex perf, SQLite concurrency

Exit code propagation (same pattern as existing modules):
- wget_cmd: run() and run_stdout() now exit on failure
- container: docker_logs, kubectl_pods/services/logs now check
  status before parsing JSON (was showing "No pods found" on error)
- pnpm_cmd: replace bail!() with eprint + process::exit in
  run_list and run_install

Performance:
- grep_cmd: compile context regex once before loop instead of
  per-line in clean_line() (was N compilations per grep call)

Data integrity:
- tracking: add PRAGMA journal_mode=WAL and busy_timeout=5000
  to prevent SQLite corruption with concurrent Claude Code instances

Signed-off-by: Patrick <patrick@rtk.ai>
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>

* fix: address review findings on P1 fixes

- tracking: WAL pragma non-fatal (NFS/read-only compat)
- wget: forward raw stderr on failure, track raw==raw (no fake savings)
- container: remove stderr shadow in docker_logs, add empty-stderr
  guard on all 4 new exit code paths for consistency with prisma pattern

Signed-off-by: Patrick <patrick@rtk.ai>
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>

---------

Signed-off-by: Patrick <patrick@rtk.ai>
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
… (rtk-ai#630)

* fix: raise output caps for grep, git status, and parser fallback (rtk-ai#617, rtk-ai#618, rtk-ai#620)

- grep: per-file match cap 10 → 25, global max 50 → 200
- git status: file list caps 5/5/3 → 15/15/10
- parser fallback: truncate 500 → 2000 chars across all modules

These P0 bugs caused LLM retry loops when RTK returned less signal
than the raw command, making RTK worse than not using it.

Fixes rtk-ai#617, rtk-ai#618, rtk-ai#620

Signed-off-by: Patrick <patrick@rtk.ai>
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>

* fix: update README example and add truncation tests for modified/untracked

- parser/README.md: update example from 500 → 2000 to match code
- git.rs: add test_format_status_modified_truncation (cap 15)
- git.rs: add test_format_status_untracked_truncation (cap 10)

Signed-off-by: Patrick <patrick@rtk.ai>
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>

* refactor: extract output caps into [limits] config section

Move hardcoded caps into config.toml so users can tune them:

  [limits]
  grep_max_results = 200      # global grep match limit
  grep_max_per_file = 25      # per-file match limit
  status_max_files = 15       # staged/modified file list cap
  status_max_untracked = 10   # untracked file list cap
  passthrough_max_chars = 2000 # parser fallback truncation

All 8 modules now read from config::limits() instead of hardcoded
values. Defaults unchanged from previous commit.

Signed-off-by: Patrick <patrick@rtk.ai>
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>

---------

Signed-off-by: Patrick <patrick@rtk.ai>
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
Tee files written to ~/.local/share/rtk/tee/ may contain sensitive
command output (tokens, credentials in error messages, API responses).
Restrict permissions on Unix to prevent other users/processes reading:

- Tee directory: chmod 0700 (owner rwx only) on creation
- Tee files: chmod 0600 (owner rw only) after each write
- Both changes guarded by #[cfg(unix)] — Windows behaviour unchanged

Also updates the hint line to: "[full output: <path> — may contain
sensitive data]" to alert LLM consumers.

Adds test_write_tee_file_permissions (unix) verifying both modes.
All 935 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Robot Taylor <955129+rjamestaylor@users.noreply.github.com>
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.

3 participants