Skip to content

[P1] Harden credential storage, profile/state paths, and daemon lifecycle #39

Description

@detrin

Context

brow intentionally handles sensitive browser profiles, cookies, localStorage, network responses, and arbitrary eval code. The current local-only architecture reduces remote exposure, but several boundaries should be explicit and tested:

  • Profile and state names are joined directly to filesystem paths.
  • Profile deletion uses recursive removal on the derived path.
  • State JSON containing authentication tokens is written with default filesystem permissions.
  • The daemon PID file stores only an integer; a stale/reused PID could refer to an unrelated process.
  • eval, authenticated fetches, and captured response previews can expose secrets by design, but there is no consolidated threat-model documentation.

Goal

Make the local security model defensible without removing the power-user capabilities that distinguish brow.

Files

  • brow/src/brow/profiles.py
  • brow/src/brow/config.py
  • brow/src/brow/daemon.py
  • brow/src/brow/routes/profiles.py
  • brow/src/brow/routes/eval.py
  • brow/src/brow/routes/browser.py
  • Corresponding tests under brow/tests/
  • Create: SECURITY.md
  • Add or update a docs page covering security and credential handling

Implementation checklist

  • Define and test a profile/state naming policy, for example a conservative [A-Za-z0-9._-]+ allowlist.
  • Resolve every derived path and reject it unless it remains strictly below the configured profiles/states root.
  • Reject symlink-based escapes and cover them with tests where the platform supports symlinks.
  • Create BROW_HOME, profiles, and states directories with private permissions on POSIX.
  • Write state files atomically with owner-only permissions because they contain cookies/tokens.
  • Ensure failed writes cannot leave a partially written credential file.
  • Store enough daemon identity metadata to distinguish brow from an unrelated process before sending a termination signal.
  • Keep the daemon bound to loopback by default and test that configuration.
  • Document that eval is arbitrary local code execution and authenticated fetch/network output may contain secrets.
  • Document safe practices: never commit state files, use dedicated low-privilege accounts where possible, delete profiles/states when finished, and review generated playbooks before replay.
  • Add a private vulnerability reporting path in SECURITY.md and enable GitHub private vulnerability reporting if desired.

Required tests

  • Reject ../escape, absolute paths, encoded separators, empty names, and path aliases.
  • Confirm valid existing names continue to work.
  • Confirm state files are private on POSIX.
  • Confirm a stale/unrelated PID is not terminated.
  • Confirm normal daemon start/status/stop still works.

Acceptance criteria

  • User-controlled names cannot read, write, or delete outside BROW_HOME.
  • Stored authentication state is private by default on supported POSIX systems.
  • brow daemon stop only terminates a verified brow daemon.
  • The repository clearly documents trusted-client assumptions and high-risk commands.
  • Security behavior is covered by automated tests.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions