Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix: harden path traversal, env injection, and tool security defenses - #77

Merged
joshuajbouw merged 7 commits into
mainfrom
fix/home-path-traversal
Feb 18, 2026
Merged

joshuajbouw merged 7 commits into
mainfrom
fix/home-path-traversal

Conversation

@joshuajbouw

@joshuajbouw joshuajbouw commented Feb 18, 2026

Copy link
Copy Markdown
Member

Closes #59

Summary

Comprehensive security hardening addressing path traversal via $HOME/$ASTRID_HOME, environment variable injection across hooks/MCP/plugins, and tool input validation. Includes the spark agent identity feature.

Critical fixes

  • UTF-8 panic in read_file — line truncation now uses char-boundary-safe slicing instead of raw byte offset
  • Double spark onboarding — removed duplicate run_spark_onboarding() call (was in both main.rs and chat.rs)
  • Blocking flock in asyncspark_tool evolve action wrapped in spawn_blocking to avoid stalling Tokio
  • Test env var leak on panic — all env-mutating tests now use EnvGuard RAII for panic-safe cleanup

Security hardening

  • Shared env blocklist — new astrid_core::env_policy module with comprehensive blocklist (union of all three inline lists + ld_/dyld_/npm_config_ prefix matching). Replaces divergent inline lists in hooks, MCP server, and plugins
  • HOME absolute validationHOME now checked with is_absolute() matching the existing ASTRID_HOME guard
  • Context env var filtering — hook sandbox now filters context.to_env_vars() through the blocklist (previously bypassed all filtering)
  • HOME relay validationHOME validated for traversal before passing to hook child processes
  • Absolute path enforcementread_file, write_file, edit_file tools reject non-absolute paths
  • JS bridge Windows fixpluginConfigBase check uses path.sep instead of hardcoded "/"

Minor improvements

  • Gateway SparkConfig field-parity test
  • SparkLockGuard RAII with lock file cleanup on drop
  • Sanitize spark before format_spark in evolve response
  • Document spark preamble + custom prompt prepend behavior

Out-of-scope issues filed

Test plan

  • cargo check --workspace — 0 errors, 0 warnings (except pre-existing QuickJS placeholder)
  • cargo test --workspace — all tests pass (1400+ across all crates)
  • cargo fmt --check — no formatting issues
  • Manual: verify spark onboarding only prompts once on astrid chat
  • Manual: verify read_file with emoji-heavy file >2000 bytes/line doesn't panic
  • Manual: verify relative paths rejected by read_file/write_file/edit_file

Reject HOME values containing '..' path segments at startup and add a
defense-in-depth prefix check in writeConfigFile to ensure the resolved
config directory stays under $HOME/.astrid/plugins/.

Closes #59
Manifest env: entries are applied to the child process with no filtering.
A malicious manifest could override HOME, PATH, NODE_OPTIONS, etc. to
escape the sandbox. Block these with a warning until the install-time
elicitation flow is implemented.

Part of #59
- bridge: refuse to start when HOME is unset instead of falling back to
  world-writable /tmp
- plugins: expand blocked env var list with proxy vars, TMPDIR, TLS/CA
  cert overrides (NODE_EXTRA_CA_CERTS, SSL_CERT_*)
- hooks: prevent custom env from overriding sandboxed allowlist vars
  (PATH, HOME, etc.) in CommandHandler
- core: reject '..' path components in HOME and ASTRID_HOME in
  AstridHome::resolve()
- mcp: add env var blocklist for library/code injection vars in
  connect_stdio_server()

Part of #59
- hooks: add BLOCKED_HOOK_ENV list and is_blocked_hook_env() to prevent
  LD_PRELOAD, NODE_OPTIONS, BASH_ENV, OPENSSL_CONF, and LD_/DYLD_/
  npm_config_ prefix injection in sandboxed hooks
- hooks: extract apply_env() helper to satisfy clippy::too_many_lines
- mcp: align server blocklist with the plugin blocklist (add HOME, PATH,
  ASTRID_HOME, proxy vars, TLS/CA vars, TMPDIR, OPENSSL_CONF)
- plugins: add ASTRID_HOME, OPENSSL_CONF to blocklist; add LD_/DYLD_/
  npm_config_ prefix blocking
- core: fix test pollution — add EnvGuard RAII type so HOME is restored
  even on panic unwind

Part of #59
Addresses all findings from three parallel security/correctness/architecture
reviews of the home-path-traversal branch.

Critical fixes:
- Fix UTF-8 panic in read_file line truncation (char boundary check)
- Remove double spark onboarding call (main.rs + chat.rs)
- Wrap blocking flock in spark_tool with spawn_blocking
- Use EnvGuard RAII in all env-mutating tests (panic-safe cleanup)

Security hardening:
- Extract shared env_policy module in astrid-core with comprehensive
  blocklist (union of hooks/MCP/plugins lists + prefix matching)
- Replace all three inline blocklists with shared is_blocked_spawn_env()
- Validate HOME is absolute before use (matching ASTRID_HOME guard)
- Filter context env vars through blocklist in hook sandbox
- Validate HOME before relaying to hook child processes
- Enforce absolute paths in read_file, write_file, edit_file tools
- Fix JS bridge path separator for Windows (use path.sep)

Minor improvements:
- Add gateway SparkConfig field-parity test
- Add SparkLockGuard RAII with lock file cleanup on drop
- Sanitize spark before format_spark in evolve response
- Document spark preamble + custom prompt interaction

Also includes spark agent identity feature (onboarding, config, tool,
system prompt integration).
native-tls 0.2.17 has a non-exhaustive match bug on newer Rust toolchains. astrid-llm was overriding the workspace reqwest (which uses rustls-tls) with bare defaults that pulled in native-tls. teloxide also defaulted to native-tls. Switch both to use workspace deps with rustls, eliminating native-tls entirely.
@joshuajbouw

Copy link
Copy Markdown
Member Author

Weird. Not sure what happened, but a dependency appears to be failing for all PRs now and I don't know if this is some weird dependency chain attack which I'm unsure is possible. Before, this all was working fine with this dependency. Anyways, I'll include it into this PR, and rebase all other PRs on top of this one.

@joshuajbouw

Copy link
Copy Markdown
Member Author

Related to the failed check and clippy: rust-native-tls/rust-native-tls#367

Will switch to rustls.

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.

bridge: writeConfigFile vulnerable to HOME path traversal

1 participant