Skip to content

Conversation

@amannhq
Copy link

@amannhq amannhq commented Jan 20, 2026

This PR implements session persistence with automatic state save/restore, encryption support, and comprehensive state management commands.

Resolves #42
Resolves #86
Resolves #115
Resolves #158

Features

1. Session Persistence (--session-name)

Automatically save and restore cookies/localStorage across browser restarts:

# Auto-save/load state for "twitter" session
agent-browser --session-name twitter open twitter.com

# Login once, then state persists automatically
# State files stored in ~/.agent-browser/sessions/

#### 2. State Encryption (AES-256-GCM)

Encrypt sensitive session data at rest:

```bash
# Generate key: openssl rand -hex 32
export AGENT_BROWSER_ENCRYPTION_KEY=<64-char-hex-key>

# State files are now encrypted automatically
agent-browser --session-name secure open example.com

3. State Management Commands

Command Description
state list List saved state files with size, date, encryption status
state show <file> Show state summary (cookies, origins, domains)
state rename <old> <new> Rename a state file
state clear <session> Clear states for a specific session name
state clear --all Clear all saved states
state clean --older-than <days> Delete states older than N days
state save <path> Manual save to custom path
state load <path> Manual load from custom path

4. Auto-Expiration

Automatically clean up old state files:

export AGENT_BROWSER_STATE_EXPIRE_DAYS=7  # Default: 30

# Or manually clean old states
agent-browser state clean --older-than 7

#### 5. Session Name Validation

Security hardening to prevent path traversal attacks:
- Only alphanumeric, hyphens, underscores allowed
- Rejects `../`, spaces, slashes, special characters

Environment Variables

Variable Description
AGENT_BROWSER_SESSION_NAME Auto-save/load state persistence name
AGENT_BROWSER_ENCRYPTION_KEY 64-char hex key for AES-256-GCM encryption
AGENT_BROWSER_STATE_EXPIRE_DAYS Auto-delete states older than N days (default: 30)

CLI Click Command with --new-tab Flag

How It Works

1. User Input

agent-browser click #button --new-tab

amannhq and others added 8 commits January 14, 2026 02:20
- Implement session name validation to ensure only safe characters are used.
- Add support for session state persistence with auto-save/load functionality.
- Introduce encryption for saved state files using AES-256-GCM.
- Forward encryption key and debug flag from environment variables to the daemon.
- Update command-line flags parsing to handle session names and validation errors.
- Improve output handling to display warnings for issues like decryption failures.
- Refactor state management utilities for better organization and reusability.
- Enhance documentation to reflect new session management features and usage examples.
… errors

- Fixed duplicate function signatures in connection.rs and main.rs
- Created validation.rs module for session name validation
- Fixed syntax errors in daemon.ts, flags.rs, and output.rs
- Removed duplicate imports in actions.ts and browser.ts
- All tests passing: 182 TypeScript + 114 Rust
- Session persistence with encryption working correctly
…gement features, including session name functionality, state encryption, management commands, and performance optimizations.
@vercel
Copy link
Contributor

vercel bot commented Jan 20, 2026

@amannhq is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

amannhq and others added 5 commits January 21, 2026 00:23
@ctate
Copy link
Collaborator

ctate commented Jan 22, 2026

Appreciate the work on session persistence! The encryption implementation looks solid (AES-256-GCM, proper auth tags, random IVs), but there are critical issues:

Security: Path Traversal Vulnerability

src/state-utils.ts:54-62

export function getAutoStateFilePath(sessionName: string, sessionId: string): string | null {
  if (!sessionName) return null;
  if (!isValidSessionId(sessionId)) {  // Validates sessionId
    throw new Error(...);
  }
  const sessionsDir = ensureSessionsDir();
  return path.join(sessionsDir, `${sessionName}-${sessionId}.json`);  // sessionName not validated
}

Attack vector:
The daemon (not CLI) reads AGENT_BROWSER_SESSION_NAME from environment at three locations:

  • daemon.ts:337 (auto-launch)
  • daemon.ts:356 (explicit launch)
  • daemon.ts:370 (close/save)

None validate before passing to getAutoStateFilePath(). An attacker can:

# Bypass CLI validation entirely
export AGENT_BROWSER_SESSION_NAME="../../../etc/passwd"
node dist/daemon.js  # Daemon starts with malicious session name

Required fix:

export function getAutoStateFilePath(sessionName: string, sessionId: string): string | null {
  if (!sessionName) return null;

  // Add this validation
  if (!isValidSessionName(sessionName)) {
    throw new Error(
      `Invalid session name '${sessionName}'. Only alphanumeric characters, hyphens, and underscores are allowed.`
    );
  }

  if (!isValidSessionId(sessionId)) {
    throw new Error(...);
  }
  // ... rest
}

Missing Tests for Cryptographic Code

encryption.ts (111 lines of crypto code) has zero unit tests. This is a blocker. Need tests for:

  • Round-trip: decryptData(encryptData(plain, key), key) === plain
  • Tampered auth tag throws error
  • Wrong key throws error
  • IV uniqueness (encrypt same data twice → different IVs)
  • Invalid key format handling
  • Malformed encrypted payload detection

Please fix the path traversal vulnerability and add crypto tests before this can be merged.

amannhq and others added 7 commits January 25, 2026 01:19
- Combined upstream features (extensions, profile, proxy, proxy_bypass, args, user_agent, provider) with session features (session_name, encryption, state management commands)
- Added state list, show, clear, clean, rename commands
- Added session name validation
- Combined persistent profile and session persistence documentation
- Fixed TypeScript type definitions to include all launch options
…or duplicated code

## Security Fixes

### 1. Path Traversal Prevention (ALREADY FIXED - Verified)
The original security concern about path traversal in `getAutoStateFilePath` was
already addressed. Verification confirms:

- `state-utils.ts:70-74`: `isValidSessionName()` validation added
- `daemon.ts:373-377, 406-408, 426-428`: All 3 daemon entry points now validate
  session names from environment variables before use
- Attack vector `AGENT_BROWSER_SESSION_NAME="../../../etc/passwd"` is now blocked

### 2. Cryptographic Tests (ALREADY FIXED - Verified)
The `encryption.test.ts` file contains 37 comprehensive tests covering:
- Round-trip encryption/decryption
- IV uniqueness verification (same data encrypted twice produces different IVs)
- Tampered auth tag detection
- Tampered ciphertext detection
- Tampered IV detection
- Wrong key handling
- Malformed payload detection
- Key format validation

### 3. StreamServer Network Exposure (NEW FIX)
- Location: `stream-server.ts:90`
- Issue: WebSocket server was binding to `0.0.0.0` (all interfaces)
- Risk: Network exposure of browser viewport and input injection (mouse/keyboard)
- Fix: Now explicitly binds to `127.0.0.1` (localhost only)

### 4. Prototype Pollution Prevention (NEW FIX)
- Location: `browser.ts:544-548`
- Issue: Object spread `{...requestHeaders, ...headers}` vulnerable to prototype
  pollution if headers contain `__proto__`, `constructor`, or `prototype` keys
- Fix: Added `safeHeaderMerge()` utility that filters dangerous keys and returns
  a null-prototype object

## Code Quality: Duplicated Code Refactored

### Consolidated into `state-utils.ts`:

1. **`listStateFiles()`** - Replaces repeated `fs.readdirSync().filter('.json')`
   pattern in daemon.ts and actions.ts

2. **`cleanupExpiredStates(days)`** - Consolidates identical cleanup logic from:
   - `daemon.ts` (startup cleanup)
   - `actions.ts` (state clean command)

3. **`safeHeaderMerge(base, override)`** - Security utility for header merging

### Removed Dead Code:
- `daemon.ts:loadStateFromFile()` - Was unused, identical to `readStateFile()`

## Test Coverage
- Added 11 new tests for security utilities (268 total, up from 257)
- Tests cover prototype pollution filtering, null-prototype objects, edge cases
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
fix:  security review addressed
@amannhq
Copy link
Author

amannhq commented Jan 24, 2026

@ctate
@Levix

Hey Chris, thanks for the thorough security review! I've addressed all the concerns. Here's a summary:


Issues You Raised

1. Path Traversal Vulnerability - FIXED

Identified that sessionName wasn't being validated in getAutoStateFilePath(), allowing attackers to bypass CLI validation via environment variables.

Fix applied:

  • Added isValidSessionName() validation in getAutoStateFilePath() (state-utils.ts:70-74)
  • All 3 daemon entry points now validate session names from environment variables before use
  • Attack vector AGENT_BROWSER_SESSION_NAME="../../../etc/passwd" is now blocked

Test coverage: 22 tests covering path traversal attempts, special characters, Unicode homograph attacks, etc.

2. Missing Crypto Tests - FIXED

The encryption.ts module now has comprehensive test coverage in encryption.test.ts (37 tests):

Requirement Status
Round-trip: decrypt(encrypt(plain)) === plain ✓ 5 tests
Tampered auth tag throws error
Wrong key throws error ✓ 2 tests
IV uniqueness (same data → different IVs) ✓ 2 tests
Invalid key format handling ✓ 7 tests
Malformed encrypted payload detection ✓ 6 tests

Additional Issues Found & Fixed

During the security audit, I discovered two more issues:

3. StreamServer Network Exposure - FIXED

The WebSocket server was binding to 0.0.0.0, exposing browser input injection to the network.

- this.wss = new WebSocketServer({ port: this.port });
+ this.wss = new WebSocketServer({ port: this.port, host: '127.0.0.1' });

4. Prototype Pollution Risk - FIXED

Header merging used object spread which was vulnerable to __proto__ injection.

- headers: { ...requestHeaders, ...headers }
+ headers: safeHeaderMerge(requestHeaders, headers)

The new safeHeaderMerge() utility filters dangerous keys (__proto__, constructor, prototype) and returns a null-prototype object.


Code Quality Improvements

Also refactored duplicated code while I was in there:

  • Consolidated listStateFiles() and cleanupExpiredStates() into state-utils.ts
  • Removed dead code (loadStateFromFile() was unused)

Let me know if you'd like any changes!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants