Back to skill

Security audit

Ebrun Original News

Security checks for vulnerabilities and agentic risk

Overview

This news skill mostly matches its stated purpose, but it also performs automatic update checks and uses an unsafe shared temp cache, so it should be reviewed before install.

Before installing, confirm you are comfortable with this skill contacting Ebrun for news and also checking Ebrun/GitHub/Gitee for skill version data. Treat any update prompt as advisory only, verify the repository yourself, and prefer disabling or fixing the update checker/cache behavior before use in shared or sensitive environments.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (3)

T01 · Skill Instruction Hijacking

Error
Location
SKILL.md:162
Finding
Mandatory Promotional and Update-Solicitation Content Injected into Agent Responses<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:162, 180-193, 202-249, 261` **Additional Location**: `examples.md:22-37, 180-197, 276, 336` **Vulnerability Type**: Mandatory output manipulation and traffic diversion **Risk Level**: High ### Vulnerable Code Snippet From `SKILL.md:180-193`: ```markdown #### 按用户要求查询文章时的格式化输出 ```markdown 📰 亿邦原创新闻 | {channel_name} {sub_channel_name} 获取时间: {current_time} --- [{title}]({url}) {author} · {publish_time} {summary} --- 更多资讯请见[亿邦官网](https://www.ebrun.com/) ``` ``` From `SKILL.md:224-249`: ```markdown **追加更新提示(如检测到新版本):** 如果步骤4检测到新版本可用,在页脚后追加。需要根据 `status` 使用不同模板: #### 场景A:本轮刚检查到新版本(`status != cached`) ```markdown --- ### 技能更新 发现 `ebrun-original-news` 有新版本 `v{latest_version}`。 回复“帮我更新 ebrun-original-news 技能”即可开始更新。 更新地址:[GitHub]({update_url_github}) | [Gitee]({update_url_gitee}) ``` #### 场景B:本轮未重新检查,沿用缓存继续提醒(`status == cached`) ```markdown --- ### 技能更新 可用更新:`ebrun-original-news v{latest_version}`。 ``` ``` ### Technical Analysis The Skill instructions require the Agent to append a fixed link to the publisher's website to ordinary news responses. They also require an update solicitation and links to external repositories whenever the separately executed version check reports a different version. Returning links to the articles explicitly requested by the user is necessary for the declared news-retrieval function. A fixed promotional footer and an unsolicited request to update the Skill are not necessary to retrieve or present those articles. These instructions therefore alter the Agent's final response for the publisher's benefit rather than solely satisfying the user's request. The templates are reinforced in `examples.md`, where the footer is presented as expected output and the Agent is directed to append update notices. This makes the behavior persistent whenever the Skill is loaded and followed. ### Attack Path 1. A user invokes the Skill to request current news. 2. The Agent loads a ...[truncated 1029 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the mandatory Ebrun homepage footer from the normal response template. 2. Return only the original article links needed to identify the requested sources. 3. Do not perform or disclose update checks during ordinary news requests unless the user explicitly asks about updates. 4. Move update functionality to a separate, explicitly invoked administrative command. 5. Do not instruct the Agent to solicit an update through natural-language replies. 6. If attribution is legally required, label it transparently and include it only where necessary rather than as a promotional footer. 7. Remove the corresponding injected templates and requirements from `examples.md`. 8. Ensure final-answer formatting remains subordinate to the user's requested format and does not introduce unrelated calls to action. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/update.py:34
Finding
Predictable Shared Python Cache Allows Version-State Poisoning and Symlink File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `scripts/update.py:34, 107-116, 174` **Vulnerability Type**: Unsafe predictable temporary file **Risk Level**: Medium ### Vulnerable Code Snippet ```python CACHE_FILE = Path(tempfile.gettempdir()) / f'{SKILL_NAME}-version-cache.json' ``` ```python def read_cache_info() -> Dict[str, Any]: try: data = json.loads(CACHE_FILE.read_text(encoding='utf-8')) except FileNotFoundError: return {} except json.JSONDecodeError: return {} return data if isinstance(data, dict) else {} ``` ```python CACHE_FILE.write_text(json.dumps(cache_payload, ensure_ascii=False, indent=2) + '\n', encoding='utf-8') ``` ### Technical Analysis The update checker stores state at a fixed, publicly predictable path in the system temporary directory. It neither verifies that the cache is a regular file owned by the current user nor rejects symbolic links. The read path accepts any syntactically valid JSON object at that location. Fields such as `last_check_time`, `last_known_version`, `last_check_source`, and `last_update_available` influence whether the checker skips networking and what update status it reports. The write path uses `Path.write_text`, which follows an existing symbolic link. An attacker who can create the predictable cache path before the victim runs the Skill can therefore point it at another file writable by the victim process. The update checker may then truncate and replace that target with cache JSON. ### Attack Path #### Cache Poisoning 1. A local attacker creates `/tmp/ebrun-original-news-version-cache.json` before the victim invokes the update checker. 2. The attacker inserts valid JSON with a recent `last_check_time`, a matching `version_api_url`, an attacker-selected `last_known_version`, and `last_update_available` set to `true`. 3. The victim runs `scripts/update.py` without `--force`. 4. `read_cache_info()` accepts the attacker-controlled object. 5. The interval lo ...[truncated 1131 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Store the cache in a per-user cache directory, such as a platform-appropriate directory derived from `XDG_CACHE_HOME`, rather than directly under the shared temporary directory. 2. Create the parent directory with permissions `0700`. 3. Create cache files with permissions `0600`. 4. Before reading, use `lstat()` to reject symbolic links and non-regular files. 5. Verify that the file is owned by the effective user before trusting its contents. 6. Write to a securely created temporary file in the same private directory, call `fsync()` as appropriate, and atomically replace the cache with `os.replace()`. 7. Open files using safe flags such as `O_NOFOLLOW`, where supported. 8. Validate all cache fields against strict schemas. In particular, restrict versions to a bounded semantic-version format, constrain check-source values to known constants, and validate timestamps. 9. Treat cache content only as a performance hint. Do not allow an untrusted cache to trigger user-facing update solicitations without a verified remote result. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/update.sh:14
Finding
Predictable Shared Shell Cache Allows Version-State Poisoning and Symlink File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `scripts/update.sh:14, 139-173, 184-217` **Vulnerability Type**: Unsafe predictable temporary file **Risk Level**: Medium ### Vulnerable Code Snippet ```bash CACHE_FILE="${TMPDIR:-/tmp}/ebrun-original-news-version-cache.json" ``` ```bash read_cache_file() { if [ ! -f "$CACHE_FILE" ]; then echo '{}' return 0 fi local json json=$(cat "$CACHE_FILE") if command -v python3 >/dev/null 2>&1; then if ! python3 -c 'import sys, json; data=json.loads(sys.stdin.read()); assert isinstance(data, dict)' <<< "$json" >/dev/null 2>&1; then log_warn "忽略损坏的版本缓存文件: $CACHE_FILE" echo '{}' return 0 fi elif command -v jq >/dev/null 2>&1; then if ! jq -e 'type == "object"' >/dev/null 2>&1 <<< "$json"; then log_warn "忽略损坏的版本缓存文件: $CACHE_FILE" echo '{}' return 0 fi elif [[ ! "$json" =~ ^[[:space:]]*\{ ]]; then log_warn "忽略损坏的版本缓存文件: $CACHE_FILE" echo '{}' return 0 fi echo "$json" } ``` ```bash with open(file_path, 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=2) f.write('\n') ``` ```bash cat > "$CACHE_FILE" <<EOF { "version_api_url": "$(json_escape "$version_api_url")", "last_check_time": $last_check_time, "last_known_version": "$(json_escape "$last_known_version")", "last_check_source": "$(json_escape "$last_check_source")", "last_update_available": $(json_bool "$last_update_available"), "last_version_file_url": "$(json_escape "$last_version_file_url")" } EOF ``` ### Technical Analysis The shell fallback uses the same fixed cache name under a shared temporary directory. Its read logic verifies only that the content resembles or parses as a JSON object. It does not verify ownership, permissions, regular-file status, or whether the path is a symbolic link. When Python is available, the write branch o ...[truncated 1628 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the shared fixed cache path with a private per-user cache directory. 2. Create the directory with mode `0700` and cache files with mode `0600`. 3. Reject cache paths that are symbolic links or are not regular files owned by the effective user. 4. Use a securely created temporary file in the same private directory and atomically rename it into place for every implementation branch. 5. Remove direct `cat > "$CACHE_FILE"` and direct Python writes to the predictable path. 6. Use `noclobber` and platform-appropriate no-follow protections where available, while recognizing that these are supplements rather than substitutes for a private directory and ownership checks. 7. Strictly validate cached timestamps, source names, booleans, URLs, and bounded semantic-version strings. 8. Do not use cached data alone to generate unsolicited update calls-to-action. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (28)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
Beyond news retrieval, the skill reads local version files, may write cache-like state, queries remote version sources, and exposes script-oriented operational controls. This mismatch matters because a content-fetching skill is being used as a vehicle for additional local-state and software-maintenance behavior, which broadens attack surface and complicates trust review.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
Beyond news retrieval, the skill reads local version files, may write cache-like state, queries remote version sources, and exposes script-oriented operational controls. This mismatch matters because a content-fetching skill is being used as a vehicle for additional local-state and software-maintenance behavior, which broadens attack surface and complicates trust review.

Ae1

High
Category
analysis-evasion
Content
bash scripts/update.sh --json
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
bash scripts/update.sh --json
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Agent Config Directory Access

High
Category
Agent Snooping
Content
import subprocess
from pathlib import Path

skill_dir = Path(".claude/skills/ebrun-original-news")
channel_file = skill_dir / "references" / "channel-list.json"

data = json.loads(channel_file.read_text(encoding="utf-8"))
Confidence
90% confidence
Finding
Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Agent Config Directory Access

High
Category
Agent Snooping
Content
import subprocess
from pathlib import Path

skill_dir = Path(".claude/skills/ebrun-original-news")
channel_file = skill_dir / "references" / "channel-list.json"

data = json.loads(channel_file.read_text(encoding="utf-8"))
Confidence
90% confidence
Finding
Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill instructs the agent to read local files, invoke shell/Python scripts, perform network requests, and maintain runtime cache behavior, but it does not declare any explicit tool restrictions or allowed-tools scope. In an agent environment, this expands the effective authority of a seemingly simple news skill and increases the chance of unintended file, shell, or network use beyond user expectations.

Natural-Language Policy Violations

Medium
Confidence
86% confidence
Finding
The natural-language instructions, activation phrases, and output templates are entirely specified in Chinese, with no indication that users may interact in other languages or choose a locale. This creates a locale/language constraint in the skill behavior without explicit opt-in or justification as a region-specific tool.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The listed triggers "看看有什么AI新闻" and "品牌有什么新动态" are much broader than the skill's stated ecommerce-news scope and overlap with common user requests about general AI or brand news. Although some exclusion cases are listed later, these phrases can still cause unintended invocation because they do not clearly restrict the request to 亿邦动力网 or ecommerce-related coverage.

Vague Triggers

Medium
Confidence
90% confidence
Finding
该 markdown 文件属于 SQP-1 适用范围。`看看AI新闻`、`看品牌全球化报道`、`产业有什么新动态` 这类表述接近日常通用说法,文档又未给出排除条件或更明确的触发边界,容易与普通对话重叠并造成误调用。

Skill Enumeration

Medium
Category
Agent Snooping
Content
import subprocess
from pathlib import Path

skill_dir = Path(".claude/skills/ebrun-original-news")
channel_file = skill_dir / "references" / "channel-list.json"

data = json.loads(channel_file.read_text(encoding="utf-8"))
Confidence
85% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Skill Enumeration

Medium
Category
Agent Snooping
Content
import subprocess
from pathlib import Path

skill_dir = Path(".claude/skills/ebrun-original-news")
channel_file = skill_dir / "references" / "channel-list.json"

data = json.loads(channel_file.read_text(encoding="utf-8"))
Confidence
85% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The documented version-check and update behavior expands the skill's network reach beyond its stated purpose of fetching Ebrun news, including contacting GitHub/Gitee-derived URLs. This creates unnecessary external connectivity and increases supply-chain and data exfiltration risk if an attacker can influence repository URLs or remote version metadata.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
Contacting GitHub/Gitee for version metadata is not necessary to retrieve latest Ebrun news and broadens the trust boundary to unrelated third-party infrastructure. Even without direct code execution, this can leak environment/network metadata, create an unexpected outbound channel, and expose users to spoofed update signals or repository-based supply-chain abuse.

Vague Triggers

Medium
Confidence
88% confidence
Finding
This manifest-style JSON defines aliases used for channel selection, and the alias "产业" is very generic and lacks scope constraints. As a trigger term, it could collide with many ordinary references to industry rather than specifically invoking this channel.

Description-Behavior Mismatch

Medium
Confidence
98% confidence
Finding
The skill manifest describes a user-facing capability to retrieve the latest ecommerce news from 亿邦动力网, but this file is a maintenance utility for checking the skill's own version updates. Its logic reads local version metadata, caches results, and queries remote version endpoints and repository files rather than fetching news articles. This is a semantic mismatch between the declared skill purpose and the actual code behavior in this file.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
技能清单描述该技能用于获取亿邦动力网最新电商新闻,但此脚本实现的是技能自身版本更新检查:读取本地 version.json、请求远程版本接口、探测 GitHub/Gitee 地址可达性并写入本地缓存。这些操作与“获取新闻报道”这一用户可见目的并不一致,属于独立的维护/更新功能。

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
脚本允许访问版本接口以及 GitHub、raw.githubusercontent.com、Gitee 等仓库域名,并在主流程中主动请求这些地址以判断技能版本和仓库可达性。对于一个声明用途仅为获取亿邦动力网新闻的技能,这类面向代码仓库和更新元数据的网络能力并非实现新闻抓取的直接必要条件。

Natural-Language Policy Violations

Low
Confidence
72% confidence
Finding
SQP-3 适用于所有文件类型。当前文件从标题、触发示例到输出格式均默认强制使用中文,没有看到允许用户选择语言/locale 的说明;若该技能面向通用用户环境,这可能构成未经用户选择的语言策略限制。

Description-Behavior Mismatch

Low
Confidence
92% confidence
Finding
The manifest describes a skill for retrieving the latest Ebrun e-commerce news articles when users ask for news. In this file, Examples 8 and 10 document invoking `scripts/update.py` to check remote versions and surface upgrade notices, which is a maintenance capability rather than part of the user-facing news retrieval purpose.

Context-Inappropriate Capability

Low
Confidence
90% confidence
Finding
Running `scripts/update.py --json --force` and checking remote APIs or repository version files introduces a software-update lookup capability unrelated to retrieving latest news content. For a skill whose declared purpose is to fetch Ebrun news reports, this maintenance/network behavior is not obviously required by that purpose.

Natural-Language Policy Violations

Low
Confidence
89% confidence
Finding
This markdown file uses Chinese throughout and does not indicate that the skill is limited to Chinese-speaking users or provide an opt-in language choice. Under the language/locale policy, forcing a single language without user choice can be a natural-language policy violation.

Intent-Code Divergence

Low
Confidence
71% confidence
Finding
The document frames the built-in scripts as defensive wrappers for news fetching, yet later sections describe separate update/version-check logic with different remote endpoints and fallback repository probing. This creates an intent-level inconsistency within the documentation about the skill's operational scope.

Natural-Language Policy Violations

Low
Confidence
83% confidence
Finding
All natural-language labels and aliases in the configuration are in Chinese, and the file does not indicate that language selection is optional or that the skill is intentionally limited to a Chinese-speaking context. This can constitute a language/locale policy concern when users are not given an explicit opt-in or alternative.

Natural-Language Policy Violations

Low
Confidence
94% confidence
Finding
This code file contains natural-language strings that force a specific language/locale for usage instructions and CLI interaction. Under the policy, a skill should not impose a language without giving the user a choice unless the constraint is clearly documented and justified as region-specific.

Static analysis

No suspicious patterns detected.