T09 · Insecure Skill Coding Practices
- Location
scripts/kw_graph.py:34- Finding
Bearer Credential Can Be Sent to an Arbitrary Environment-Controlled Endpoint
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill is mostly a read-only keyword-graph helper, but its documentation and code expose broader local-file, Elasticsearch, and configurable network behaviors than the primary 2brain-only framing suggests.
Review this skill before installing. Use it only with a trusted 2brain API base and avoid setting TWOBRAIN_API_BASE to arbitrary or http URLs. Do not enable the local or Elasticsearch backends unless you intentionally want the agent to read that folder or query that ES endpoint. Treat ~/.config/2brain-keyword-map/config.json as a sensitive token file, and be cautious opening generated HTML graphs from untrusted keyword data until the renderer escapes content safely.
scripts/kw_graph.py:34Bearer Credential Can Be Sent to an Arbitrary Environment-Controlled Endpoint
scripts/kw_graph.py:228Backend-Controlled Keywords Enable Stored Script Injection in Generated HTML Graphs
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
)
print("… 正在向 2brain 拉取关键词树,请稍候", file=sys.stderr)
try:
with urllib.request.urlopen(req, timeout=30) as r:
payload = json.loads(r.read())
except urllib.error.HTTPError as e:
body = e.read().decode("utf-8", "replace")[:200]
The documented behavior extends beyond the stated 2brain-only scope to include local document directories and Elasticsearch, despite repeated claims that the skill does not route to local files or other sources. This kind of deceptive or inconsistent scoping is dangerous because operators may approve the skill under a narrower threat model than the one it actually implements.
The documented behavior extends beyond the stated 2brain-only scope to include local document directories and Elasticsearch, despite repeated claims that the skill does not route to local files or other sources. This kind of deceptive or inconsistent scoping is dangerous because operators may approve the skill under a narrower threat model than the one it actually implements.
The manifest framing says the skill only reads a 2brain KB by base_id, while the documentation later exposes unrelated local-folder and Elasticsearch backends that generate keyword trees from other corpora. This is a security-relevant integrity issue because downstream policy engines, reviewers, or users may rely on the manifest to understand the trust boundary and data exposure surface.
The documentation explicitly says the skill does not route to local files, but later describes a local backend that reads .txt/.md files from a folder and computes a tree. This contradiction can cause unanticipated local data exposure if the agent or user trusts the earlier statement and invokes the skill in environments containing sensitive files.
The skill manifest says the backend is currently 2brain-only and positions the tool as a read-only KB graph reader, but the code silently supports additional local and Elasticsearch backends. This creates undeclared data-access capability expansion: a caller or attacker controlling args/config can make the skill read local corpora or query external services outside the stated trust boundary, violating least privilege and user expectations.
The skill declares no explicit tool/permission scope even though the documentation shows use of environment variables, filesystem reads/writes, and outbound network access. In an agent setting, missing scope boundaries increases the chance the skill is invoked with broader privileges than users expect, especially because the trigger is broad and the skill later documents non-2brain backends.
The trigger phrases are broad and include common requests like 'what's in this library' or 'take a look at this library', increasing the chance of accidental invocation. Because the skill can access network resources and, per later documentation, local files or Elasticsearch, unintended triggering materially increases exposure risk.
The documentation recommends storing an API token persistently in ~/.config/2brain-keyword-map/config.json. Even with restrictive permissions, long-lived local credential storage increases the blast radius of host compromise or accidental reuse by other processes, and it conflicts somewhat with the narrow read-only positioning of the skill.
# Option A: private config file (recommended — token stays out of env & artifacts)
mkdir -p ~/.config/2brain-keyword-map
cat > ~/.config/2brain-keyword-map/config.json <<'JSON'
{ "token": "2B-your-graph-API-key", "base_id": 700 }
JSON
One section says the current backend works by base_id and the agent should ask for a base_id, but other sections expose corpus-path and Elasticsearch parameter modes. This inconsistency weakens safe routing guarantees and may let the skill operate on unintended sources if the agent follows one section while the implementation supports broader behavior.
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.
**Credential discipline (hard rule)**:
- The token is sent only as an `Authorization: Bearer` header to the fixed 2brain endpoint;
- **never** print the token to the conversation, write it into HTML/JSON artifacts, logs, or URL query;
- the engine guarantees artifacts contain no token; don't repeat the key when relaying results.
**Output discipline**:
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.
# 方式一:私有配置文件(推荐,token 不进环境/不落产物)
mkdir -p ~/.config/2brain-keyword-map
cat > ~/.config/2brain-keyword-map/config.json <<'JSON'
{ "token": "2B-你的图谱APIKey", "base_id": 700 }
JSON
The documentation instructs users to persist an API token in a plaintext config file under the home directory and also suggests environment variables as an alternative. While file permissions are restrictive, long-lived plaintext secret persistence increases the blast radius if the user account, backups, dotfiles, or local filesystem are later exposed; the skill context makes this somewhat less dangerous because the token is intended only for a read-only keyword-graph API, but it still grants access to backend data for the configured base.
# 1. 把 kb-keyword-graph/ 放进 Agent 的 skills 目录
# OpenClaw: ~/.openclaw/<workspace>/skills/kb-keyword-graph/
# 2. 配置凭证(二选一)
mkdir -p ~/.config/2brain-keyword-map
printf '{ "token": "2B-你的图谱Key", "base_id": 700 }' > ~/.config/2brain-keyword-map/config.json
chmod 700 ~/.config/2brain-keyword-map && chmod 600 ~/.config/2brain-keyword-map/config.json
# 或: export TWOBRAIN_GRAPH_KEY=2B-... ; export TWOBRAIN_BASE_ID=700
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
# 2. 配置凭证(二选一)
mkdir -p ~/.config/2brain-keyword-map
printf '{ "token": "2B-你的图谱Key", "base_id": 700 }' > ~/.config/2brain-keyword-map/config.json
chmod 700 ~/.config/2brain-keyword-map && chmod 600 ~/.config/2brain-keyword-map/config.json
# 或: export TWOBRAIN_GRAPH_KEY=2B-... ; export TWOBRAIN_BASE_ID=700
# 课程环境: export TWOBRAIN_API_BASE=https://test.2brain.ai/api
# 3. 自检
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
# 2. 配置凭证(二选一)
mkdir -p ~/.config/2brain-keyword-map
printf '{ "token": "2B-你的图谱Key", "base_id": 700 }' > ~/.config/2brain-keyword-map/config.json
chmod 700 ~/.config/2brain-keyword-map && chmod 600 ~/.config/2brain-keyword-map/config.json
# 或: export TWOBRAIN_GRAPH_KEY=2B-... ; export TWOBRAIN_BASE_ID=700
# 课程环境: export TWOBRAIN_API_BASE=https://test.2brain.ai/api
# 3. 自检
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
# 2. 配置凭证(二选一)
mkdir -p ~/.config/2brain-keyword-map
printf '{ "token": "2B-你的图谱Key", "base_id": 700 }' > ~/.config/2brain-keyword-map/config.json
chmod 700 ~/.config/2brain-keyword-map && chmod 600 ~/.config/2brain-keyword-map/config.json
# 或: export TWOBRAIN_GRAPH_KEY=2B-... ; export TWOBRAIN_BASE_ID=700
# 课程环境: export TWOBRAIN_API_BASE=https://test.2brain.ai/api
# 3. 自检
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
# 2. 配置凭证(二选一)
mkdir -p ~/.config/2brain-keyword-map
printf '{ "token": "2B-你的图谱Key", "base_id": 700 }' > ~/.config/2brain-keyword-map/config.json
chmod 700 ~/.config/2brain-keyword-map && chmod 600 ~/.config/2brain-keyword-map/config.json
# 或: export TWOBRAIN_GRAPH_KEY=2B-... ; export TWOBRAIN_BASE_ID=700
# 课程环境: export TWOBRAIN_API_BASE=https://test.2brain.ai/api
# 3. 自检
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
# 2. 配置凭证(二选一)
mkdir -p ~/.config/2brain-keyword-map
printf '{ "token": "2B-你的图谱Key", "base_id": 700 }' > ~/.config/2brain-keyword-map/config.json
chmod 700 ~/.config/2brain-keyword-map && chmod 600 ~/.config/2brain-keyword-map/config.json
# 或: export TWOBRAIN_GRAPH_KEY=2B-... ; export TWOBRAIN_BASE_ID=700
# 课程环境: export TWOBRAIN_API_BASE=https://test.2brain.ai/api
# 3. 自检
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
# 2. 配置凭证(二选一)
mkdir -p ~/.config/2brain-keyword-map
printf '{ "token": "2B-你的图谱Key", "base_id": 700 }' > ~/.config/2brain-keyword-map/config.json
chmod 700 ~/.config/2brain-keyword-map && chmod 600 ~/.config/2brain-keyword-map/config.json
# 或: export TWOBRAIN_GRAPH_KEY=2B-... ; export TWOBRAIN_BASE_ID=700
# 课程环境: export TWOBRAIN_API_BASE=https://test.2brain.ai/api
# 3. 自检
该 markdown 文件适用 SQP-1。L29-L34 将“这个库里有什么”“知识结构是怎样的”“帮我看下”“这个库主要讲哪些主题”等常见自然表达直接定义为触发条件,且 L34 说明即使没说“图谱”也会触发,扩大了匹配范围。虽然文中提供了一个排歧说明,但整体仍缺少更明确的边界、负例或更具体的限定,容易与普通问答请求重叠。
This markdown file presents all operational guidance exclusively in Chinese, which effectively forces a specific language for users of the skill. The policy allows locale constraints only when the constraint is explicitly documented and justified or when users are offered a language choice, neither of which appears here.
The code introduces undeclared local and elasticsearch backends even though the skill metadata says the current backend is 2brain-only and does not route to other sources. This creates a capability gap between declared behavior and actual behavior, which is dangerous because policy, review, and user trust may rely on the manifest while the code can access local files or external services.
The local backend can read arbitrary .txt/.md files from a caller-supplied directory via rglob, which expands the skill from KB inspection into general local file access. In an agent environment, this can expose sensitive data from unintended directories and bypass the read-only, KB-scoped expectations described in the skill context.
The Elasticsearch backend allows outbound HTTP requests to a configurable es_url, enabling the skill to query arbitrary external or internal ES instances beyond the declared 2brain source. This broadens the trust boundary and can be abused for unauthorized data access or SSRF-like probing of reachable services from the runtime environment.
The code reads a local config file from the user's home directory and, together with later support for local corpus paths, expands the skill from a narrowly described remote KB graph reader into one that can inspect local resources. In context, this mismatch is dangerous because users invoking a knowledge-base visualization skill would not reasonably expect filesystem reads or arbitrary local corpus ingestion paths.
No suspicious patterns detected.