Back to skill

Security audit

BIC-QA

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed BIC-QA API client that stores an API key and local session state for its stated database and OS Q&A purpose, with one local state-file hardening caveat.

Install only if you are comfortable sending BIC-QA questions to the BIC-QA service with your API key. Keep the API key file private, avoid placing BICQA_STATE_FILE or BICQA_STATE_DIR in shared or world-writable directories, and add project-local state directories to .gitignore as the skill suggests.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/bicqa.py:191
Finding
Predictable Temporary State File with Delayed Permission Hardening## Vulnerability Details **File Location**: `scripts/bicqa.py:191-197` **Vulnerability Type**: Predictable temporary file, symlink following, and transient insecure permissions **Risk Level**: Medium ### Vulnerable Code ```python def save_state(state): p = resolve_state_path() p.parent.mkdir(parents=True, exist_ok=True) tmp = p.with_suffix(p.suffix + ".tmp") tmp.write_text(json.dumps(state, ensure_ascii=False, indent=2), encoding="utf-8") os.replace(tmp, p) try: os.chmod(p, 0o600) except OSError: pass ``` The state can contain recent user input and session identifiers, as shown at `scripts/bicqa.py:474-478`: ```python rec["last_used"] = time.time() rec["turns"] = int(rec.get("turns") or 0) + 1 rec["last_question"] = args.q[:200] if result["tokens"]: rec["tokens"] = int(rec.get("tokens") or 0) + int(result["tokens"]) save_state(state) ``` ### Technical Analysis `save_state()` constructs a predictable temporary pathname by appending `.tmp` to the configured state filename. It then opens that pathname through `Path.write_text()`, which does not request exclusive creation and follows symbolic links. The temporary file initially receives permissions derived from the process umask. Mode `0600` is applied only to the final destination after `os.replace()`. Consequently, if the configured state directory is shared or attacker-writable, a local attacker may: 1. Read the temporary file before replacement if the effective umask permits it. 2. Pre-create the predictable temporary pathname as a symbolic link. 3. Cause `write_text()` to truncate and overwrite the symlink target if the victim process has permission to write to it. The environment-controlled `BICQA_STATE_FILE` and `BICQA_STATE_DIR` settings make deployment into a shared or otherwise unsafe directory possible. Exploitation therefore requires local access to the selected directory and is not remotely achievable through the BIC-QA API alone. ### Attack Pat ...[truncated 1341 chars]
Remediation
## Remediation Suggestions 1. Create the temporary file atomically and exclusively in the destination directory using `tempfile.mkstemp()` or an equivalent secure primitive. 2. Set mode `0600` when creating the temporary file rather than after replacement. 3. Write through the returned file descriptor, flush buffered data, and call `os.fsync()` before performing `os.replace()`. 4. Reject pre-existing symbolic links and avoid reopening the temporary file by pathname after secure creation. 5. Verify that the state directory is owned by the current user and is not group- or world-writable. Reject unsafe custom state locations unless explicitly overridden with a documented warning. 6. Apply restrictive permissions to the state directory, such as `0700`, where supported. 7. Preserve cleanup logic so the temporary file is removed if serialization, writing, synchronization, or replacement fails. A secure implementation should follow this pattern: ```python import tempfile def save_state(state): p = resolve_state_path() p.parent.mkdir(parents=True, exist_ok=True, mode=0o700) fd, tmp_name = tempfile.mkstemp( prefix=p.name + ".", suffix=".tmp", dir=str(p.parent), ) try: os.fchmod(fd, 0o600) data = json.dumps(state, ensure_ascii=False, indent=2).encode("utf-8") with os.fdopen(fd, "wb") as tmp_file: fd = -1 tmp_file.write(data) tmp_file.flush() os.fsync(tmp_file.fileno()) os.replace(tmp_name, p) os.chmod(p, 0o600) finally: if fd != -1: os.close(fd) try: os.unlink(tmp_name) except FileNotFoundError: pass ```
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (7)

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill invokes a Python script, reads credential files, writes persistent session state, and makes network calls, but it declares no explicit tool scope or allowed-tools boundary. In an agent ecosystem, missing capability scoping increases the blast radius because the skill can access sensitive local files and external endpoints without a machine-readable restriction layer.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
**不要把 Key 写进 SKILL.md、脚本、命令历史或回复里。** 配置方式(bash / WSL / macOS):

```bash
mkdir -p ~/.bic/config && chmod 700 ~/.bic/config
printf '%s' '<你的Key>' > ~/.bic/config/api_key && chmod 600 ~/.bic/config/api_key
```
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Session Persistence

Medium
Category
Rogue Agent
Content
**不要把 Key 写进 SKILL.md、脚本、命令历史或回复里。** 配置方式(bash / WSL / macOS):

```bash
mkdir -p ~/.bic/config && chmod 700 ~/.bic/config
printf '%s' '<你的Key>' > ~/.bic/config/api_key && chmod 600 ~/.bic/config/api_key
```
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
```bash
mkdir -p ~/.bic/config && chmod 700 ~/.bic/config
printf '%s' '<你的Key>' > ~/.bic/config/api_key && chmod 600 ~/.bic/config/api_key
```

Windows PowerShell:
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
"NO_API_KEY",
        "未找到 BIC-QA API Key。请到 https://www.bic-qa.com 注册获取,然后二选一:\n"
        "  1) export BIC_API_KEY='<你的Key>'\n"
        "  2) printf '%s' '<你的Key>' > ~/.bic/config/api_key && chmod 600 ~/.bic/config/api_key",
    )
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
代码在请求头中固定发送 "Accept-Language: zh-CN",这会将交互语言/区域偏好强制限定为中文环境。文件中未见用户可配置选项、显式 opt-in,或仅适用于特定地区合规场景的说明。

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
The title and entire document are written as a Chinese-only quick reference, with no indication that users may choose another language or that the language restriction is required by a region-specific constraint. This can violate a language/locale policy when a skill or its instructions implicitly force a specific language without opt-in.

Static analysis

No suspicious patterns detected.