T05 · Unauthorized Access and Privilege Escalation
Warning
- Location
- scripts/agent_sync.py:303
- Finding
- Query Mode Collects Transcripts Outside the Selected Project## Vulnerability Details **File Location**: `scripts/agent_sync.py:303-311` **Vulnerability Type**: Cross-project transcript access caused by incomplete scope enforcement **Risk Level**: Medium ### Vulnerable Code ```python query_match = not args.query or session["relevance"] >= len(args.query) project_match = is_within(session.get("cwd"), project) if args.query: if not query_match: continue elif not project_match: continue session["project_match"] = project_match sessions.append(session) ``` ### Technical Analysis Session discovery recursively enumerates recent JSONL files from the user's Claude Code and Codex session stores. The code calculates whether each session's recorded working directory is within the selected project, but it only enforces that boundary when no query is supplied. When `--query` is present, a textual match replaces the project-boundary requirement. Consequently, a common, broad, or attacker-influenced query can select sessions belonging to unrelated projects. The selected session data can include visible user and assistant messages, session identifiers, working directories, and absolute source paths. This violates least privilege because a project synchronization operation can collect transcript data outside the target project's scope. Confining written packets to `.agent-sync/imports/` reduces accidental Git publication but does not prevent the unrelated data from being read, printed, copied into the target project, or exposed to a subsequent agent. ### Attack Path 1. An agent or operator invokes `sync`, `recent`, `list`, or `import` for a selected project and supplies a broad or attacker-chosen `--query`. 2. The script recursively searches all recent Claude Code and Codex transcript files under the configured session roots. 3. A transcript from an unrelated project contains the query term. 4. `project_match` evaluates to false, but the result is not enforced becaus ...[truncated 970 chars]
- Remediation
- ## Remediation Suggestions 1. Require `project_match` for all normal discovery operations, including query mode: ```python query_match = not args.query or session["relevance"] >= len(args.query) project_match = is_within(session.get("cwd"), project) if not project_match: continue if not query_match: continue ``` 2. If cross-project recovery is a legitimate use case, place it behind an explicit option such as `--include-other-projects`. Clearly warn that the option may expose unrelated and sensitive transcript content. 3. Reject sessions with missing or unparseable working-directory metadata by default instead of allowing query matches to bypass project attribution. 4. Minimize packet metadata by omitting absolute transcript source paths and unrelated working-directory paths unless the operator explicitly requests them. 5. Add best-effort redaction for common credential formats before printing or writing transcript excerpts. Continue warning that automated redaction cannot guarantee removal of every secret. 6. Add regression tests demonstrating that: - Query mode excludes sessions outside the selected project. - Broad query terms cannot bypass project isolation. - Sessions with missing working-directory metadata fail closed. - Any explicit cross-project option requires deliberate opt-in and is clearly represented in output.
