T09 · Insecure Skill Coding Practices
Warning
- Location
- xhs-keepalive.py:229
- Finding
- Authenticated Browser State Is Saved Without Restrictive File Permissions<![CDATA[ ## Vulnerability Details **File Location**: `xhs-keepalive.py`, lines 229–242 **Vulnerability Type**: Insecure storage of authenticated session state **Risk Level**: Medium ### Vulnerable Code ```python if args.action == 'save': # 先 open 一下建立 session subprocess.run( ['agent-browser', 'open', 'https://www.xiaohongshu.com/explore'], capture_output=True, text=True, timeout=30 ) r = subprocess.run( ['agent-browser', 'state', 'save', str(STATE_FILE)], capture_output=True, text=True ) print(r.stdout, r.stderr) if r.returncode == 0: ok(f"state saved to {STATE_FILE}") return r.returncode ``` The sensitivity of this state is also documented in `docs/pitfalls.md`, line 44: ```text 36 字节的 state file 是 skeleton, 实际需要大几百 KB (含 user data dir / cookies / cache) ``` ### Technical Analysis The browser state can contain reusable authentication cookies, browser cache, and other session material. The code saves that state without setting a restrictive mode on either the state file or its containing directory. By contrast, the converted cookie file is explicitly protected with mode `0600` at `xhs-keepalive.py:95`. No equivalent protection is applied to `STATE_FILE`. Its effective permissions therefore depend on the process umask and the behavior of `agent-browser`. On systems with a permissive umask, the resulting state may be readable by other local users or processes. This violates the principle that authentication material should only be accessible to its owner. ### Attack Path 1. A victim authenticates to Xiaohongshu and loads cookies into `agent-browser`. 2. The victim runs `xhs-keepalive.py state save`. 3. `agent-browser` writes authenticated state to `data/state/xhs.state`. 4. The state file receives permissions derived from the environment rather than an enforced `0600` mode. 5. A local attacker or unrelated process reads the file. 6. The attacker loads the copied browser state into ...[truncated 710 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Create the state directory with owner-only permissions: ```python STATE_FILE.parent.mkdir(parents=True, exist_ok=True, mode=0o700) os.chmod(STATE_FILE.parent, 0o700) ``` 2. Immediately enforce mode `0600` after a successful state save: ```python if r.returncode == 0: os.chmod(STATE_FILE, 0o600) ok(f"state saved to {STATE_FILE}") ``` 3. Before loading a state file, inspect its mode and refuse files that are readable or writable by group or others. 4. Document that browser-state files contain authentication credentials and must not be shared, backed up to untrusted locations, or committed to source control. 5. Add `data/state/` and other runtime credential paths to `.gitignore`. 6. Consider encrypting long-lived state at rest when the execution environment supports a suitable operating-system credential store. ]]>
