T09 · Insecure Skill Coding Practices
- Location
scripts/mubu/config.py:41- Finding
Base URL Override Allows Plaintext Transmission of Mubu Credentials and Tokens
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This Mubu integration is not clearly malicious, but it needs Review because it can use saved account credentials to read and change real remote notes while some safety claims are broader than the code enforces.
Install only if you are comfortable letting an agent operate your Mubu account. Keep MUBU_PHONE, MUBU_PASSWORD, MUBU_MEMBER_ID, and ~/.mubu_token protected; do not set MUBU_BASE_URL unless it is an HTTPS Mubu URL; and require explicit human confirmation before create, save, move, rename, export-tree, or purge actions.
scripts/mubu/config.py:41Base URL Override Allows Plaintext Transmission of Mubu Credentials and Tokens
scripts/mubu/client.py:129Token Temporary File Is Created Before Restrictive Permissions Are Applied
The declared skill purpose emphasizes Mubu–Obsidian integration, import/export, and sync, but the documented behavior includes broader account-level content management such as login, create, move, rename, save, delete, purge, and recursive export. This mismatch increases the risk that users or calling agents invoke a skill believing it is a narrow integration utility when it actually has wider destructive and account-management powers.
The declared skill purpose emphasizes Mubu–Obsidian integration, import/export, and sync, but the documented behavior includes broader account-level content management such as login, create, move, rename, save, delete, purge, and recursive export. This mismatch increases the risk that users or calling agents invoke a skill believing it is a narrow integration utility when it actually has wider destructive and account-management powers.
Referenced artifact was not completely inspected
所有操作都通过 `scripts/mubu/client.py` 中的 `MubuClient` 类完成(`scripts/mubu_api.py` 仅为向后兼容的重新导出 shim,不再建议直接使用;**不再有**独立的
The client automatically loads credentials from a local .env-style file into process environment variables during initialization. While this is common for developer convenience, it increases exposure of sensitive credentials because any code running in the same process can read them from os.environ, and silent auto-loading may cause the tool to use secrets without explicit user action.
"""幕布 API 客户端"""
def __init__(self, phone: Optional[str] = None, password: Optional[str] = None) -> None:
# T5:在读取 phone/password 之前,先尝试从 .env 文件补全凭据
self._load_env_file()
self.phone = phone or os.getenv("MUBU_PHONE")
self.password = password or os.getenv("MUBU_PASSWORD")
The _load_env_file helper is designed to read plaintext credentials from ~/.workbuddy/.env.mubu. In a skill that integrates with a remote note service, automatic ingestion of local secrets is security-relevant because it expands the trust boundary from explicit arguments to ambient local files, making accidental secret use or leakage more plausible.
self._session_id = str(uuid.uuid4())
def _load_env_file(self, path: Optional[Path] = None) -> None:
"""从 .env 文件加载凭据(仅当环境变量未设置时补全)。
默认读取 ENV_FILE(~/.workbuddy/.env.mubu);文件不存在则静默跳过。
逐行解析 KEY=VALUE,忽略空行与 # 注释行。
At this point the code resolves the env-file path and, if present, proceeds to read credentials from it with broad exception suppression. The silent behavior reduces visibility into when secrets are being sourced, which is risky in an agent/skill context because users may not realize local credentials are being consumed automatically.
仅补全 MUBU_PHONE / MUBU_PASSWORD,且环境变量已设置时优先于文件。
Args:
path: 可选,指定 .env 文件路径(便于测试;默认用 ENV_FILE)
"""
env_path = path or ENV_FILE
if not env_path.exists():
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# purge → 调用真实删除 API 后移除标记(唯一不可逆操作)。
TRASH_FILE = Path.home() / ".workbuddy" / ".mubu_trash.json"
# .env 凭据文件路径:仅当环境变量未设置时用于补全 MUBU_PHONE / MUBU_PASSWORD
ENV_FILE = Path.home() / ".workbuddy" / ".env.mubu"
# 默认请求头
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
"""校验并解析本地文件路径,仅允许当前工作目录或其子目录(安全官 #3)。
拒绝绝对路径、``..`` 越界路径、以及跳出当前工作目录的路径,防止
``create --md`` / ``save --file`` 读取 ``/etc/passwd``、``~/.ssh/id_rsa``
等任意文件并外发。校验失败抛 MubuError(清晰错误,而非原始栈)。
"""
# 0) 展开 ~ 为用户目录(如 ~/.ssh/id_rsa → /Users/.../.ssh/id_rsa),
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
"""校验并解析本地文件路径,仅允许当前工作目录或其子目录(安全官 #3)。
拒绝绝对路径、``..`` 越界路径、以及跳出当前工作目录的路径,防止
``create --md`` / ``save --file`` 读取 ``/etc/passwd``、``~/.ssh/id_rsa``
等任意文件并外发。校验失败抛 MubuError(清晰错误,而非原始栈)。
"""
# 0) 展开 ~ 为用户目录(如 ~/.ssh/id_rsa → /Users/.../.ssh/id_rsa),
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
"""校验并解析本地文件路径,仅允许当前工作目录或其子目录(安全官 #3)。
拒绝绝对路径、``..`` 越界路径、以及跳出当前工作目录的路径,防止
``create --md`` / ``save --file`` 读取 ``/etc/passwd``、``~/.ssh/id_rsa``
等任意文件并外发。校验失败抛 MubuError(清晰错误,而非原始栈)。
"""
# 0) 展开 ~ 为用户目录(如 ~/.ssh/id_rsa → /Users/.../.ssh/id_rsa),
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
``create --md`` / ``save --file`` 读取 ``/etc/passwd``、``~/.ssh/id_rsa``
等任意文件并外发。校验失败抛 MubuError(清晰错误,而非原始栈)。
"""
# 0) 展开 ~ 为用户目录(如 ~/.ssh/id_rsa → /Users/.../.ssh/id_rsa),
# 展开后若为绝对路径将在下一步被明确拒绝,避免被静默解析为 cwd 下文件。
path = os.path.expanduser(path)
# 1) 拒绝越界片段(.. 跳出目录层级)
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
def test_uses_os_rename(self, tmp_path):
tok = tmp_path / "tok.json"
# 隔离 ENV_FILE,避免读取用户真实 ~/.workbuddy/.env.mubu(其 chmod 会污染 os.chmod mock)
with mock.patch.object(mubu.client, "ENV_FILE", tmp_path / "no_env.mubu"), \
mock.patch.object(mubu.client, "TOKEN_FILE", tok):
with mock.patch("os.rename") as mren, \
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
def test_uses_os_rename(self, tmp_path):
tok = tmp_path / "tok.json"
# 隔离 ENV_FILE,避免读取用户真实 ~/.workbuddy/.env.mubu(其 chmod 会污染 os.chmod mock)
with mock.patch.object(mubu.client, "ENV_FILE", tmp_path / "no_env.mubu"), \
mock.patch.object(mubu.client, "TOKEN_FILE", tok):
with mock.patch("os.rename") as mren, \
The tests validate deliberate browser-parity header and fingerprint emulation, including stable client/session identifiers and request IDs, which is not obviously required for a simple note sync integration. This can facilitate masquerading as an interactive browser client to bypass anti-bot, telemetry, or unofficial API protections, increasing the risk of stealthy unauthorized automation against the remote service.
The manifest frames the skill as an integration utility for importing Mubu outlines into Obsidian, syncing Markdown to Mubu, and querying/exporting notes. This changelog shows the skill also implements direct Mubu login, token caching, folder/document CRUD, delete, move, rename, and purge-style management operations, which materially exceed a narrow integration/sync/export description.
The manifest says '把 Markdown 同步到幕布', which implies synchronization behavior. The changelog explicitly corrects this wording to say it is not true sync, lacks diff/merge, and repeated import creates a new copy, indicating the described behavior materially overstates what the skill actually does.
The README explicitly promotes using Mubu as an AI agent's 'long-term, structured memory' and describes automated read/write flows. Combined with cached credentials and token persistence described elsewhere, this increases the risk of sensitive data retention, unintended propagation of secrets into external services, and persistent unauthorized modifications if the agent is mis-scoped or compromised.
① Let your AI Agent read & write Mubu directly — turn Mubu into your Agent's long-term, structured memory.
python3 scripts/mubu_api.py get <doc-id> --export markdown > memory.md # Agent pulls the latest outline
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.
The trigger words are broad (幕布, mubu, outline import/export terms) and the README says the skill can be triggered automatically when these keywords appear. In an agent environment, ambiguous auto-triggering can cause the skill to activate in unintended contexts, leading to unauthorized access to Mubu data or unreviewed write actions if the agent is connected to real credentials.
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.
python3 scripts/mubu_api.py get <doc-id> --export markdown > vault/notes/mubu.md # 幕布 → Obsidian
python3 scripts/mubu_api.py create --md vault/notes/mubu.md --folder <folder-id> # Obsidian → 幕布
③ 周会纪要自动归档 —— 一步把 examples/weekly.md 推入幕布。
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.
python3 scripts/mubu_api.py get <doc-id> --export markdown > vault/notes/mubu.md # 幕布 → Obsidian
python3 scripts/mubu_api.py create --md vault/notes/mubu.md --folder <folder-id> # Obsidian → 幕布
3 周会纪要自动归档 —— 一步把 examples/weekly.md 推入幕布。
The README instructs users to install the skill via npx skills add liuboacean/mubu-integration without pinning a specific version or immutable reference. This creates a supply-chain risk: users may receive whatever package version is current at install time, including a compromised or unexpectedly broadened release.
The skill metadata frames the capability as Obsidian import/sync/export, but the README documents substantially broader functionality including login, folder traversal, search, rename, delete, and full tree export. This scope mismatch can mislead users and agents into granting or invoking more powerful account-wide operations than the stated purpose suggests.
The README exposes destructive and account-management operations such as delete, move, rename, and recursive export even though the stated use case is Obsidian integration. In an agent-triggered context, these commands materially increase the risk of unintended data loss, reorganization, or mass exfiltration beyond what users would expect from a sync/import skill.
The trigger keywords include very broad terms such as 幕布 and mubu, which can cause the skill to auto-activate in conversations that merely mention the product rather than request an action. Because the skill can access, modify, export, and delete user data, accidental activation meaningfully raises the chance of unauthorized or unintended operations.
Detected: suspicious.exposed_secret_literal