Back to skill

Security audit

内容引擎 / Content Engine

Security checks for vulnerabilities and agentic risk

Overview

The skill matches its content-publishing purpose, but its Obsidian import/export code can read or overwrite files outside the intended vault, so it needs review before installation.

Install only after reviewing the Obsidian path handling or disabling those workflows. Use least-privilege platform tokens, keep secrets out of logs and shell history where possible, and require an explicit preview plus confirmation before any public publish, schedule, delete, export, or file overwrite action.

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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/obsidian_sync.py:440
Finding

Arbitrary File Read Through Obsidian Import Path Traversal

Content
View full analysis
Remediation
View remediation
str: if not relative_path or os.path.isabs(relative_path): raise ValueError("The file path must be relative to the vault") vault = os.path.realpath(os.path.expanduser(vault_path)) target = os.path.realpath(os.path.join(vault, relative_path)) try: inside_vault = os.path.commonpath([vault, target]) == vault except ValueError: inside_vault = False if not inside_vault: raise ValueError("The file path must remain inside the vault") return target ``` Use this helper instead of directly calling `os.path.join`. In addition: 1. Reject absolute paths and traversal outside the vault. 2. Require the imported target to be a regular `.md` file. 3. Resolve symlinks before checking containment. 4. Consider rejecting symlink targets entirely when imports are expected to operate only on ordinary vault files. 5. Avoid returning unnecessary external path information in error messages. 6. Add tests for absolute paths, `../` traversal, nested traversal, symlink escapes, and valid nested vault notes. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/obsidian_sync.py:566
Finding

Arbitrary File Write Through Obsidian Export Path Traversal

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/content_store.py:591
Finding

Arbitrary File Read Through Content Store Obsidian Import

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (68)

Tainted flow: 'req' from os.environ.get (line 290, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
95% confidence
Finding

The code places the WeChat app secret directly into the URL query string when requesting an access token. Query parameters are commonly exposed in proxy logs, monitoring systems, exception messages, and upstream infrastructure, so this increases the chance of credential disclosure even though the destination host is legitimate.

Content

Scanner excerpt · scripts/metrics_collector.py (reported line 270)May include surrounding context.

python
f"grant_type=client_credential&appid={appid}&secret={secret}"
        )
        req = Request(token_url, method="GET")
        with urlopen(req, timeout=30) as resp:
            token_data = json.loads(resp.read().decode("utf-8"))

        if "access_token" not in token_data:

Tainted flow: 'req' from os.environ.get (line 290, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/metrics_collector.py (reported line 168)May include surrounding context.

python
req = Request(url, data=body, headers=headers, method=method)

    try:
        with urlopen(req, timeout=30) as resp:
            resp_data = resp.read().decode("utf-8")
            return json.loads(resp_data) if resp_data else {}
    except HTTPError as e:

Tainted flow: 'req' from os.environ.get (line 290, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/metrics_collector.py (reported line 296)May include surrounding context.

python
req = Request(url, data=body, headers=headers, method=method)

    try:
        with urlopen(req, timeout=30) as resp:
            resp_data = resp.read().decode("utf-8")
            return json.loads(resp_data) if resp_data else {}
    except HTTPError as e:

Tainted flow: 'req' from os.environ.get (line 290, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/publisher.py (reported line 121)May include surrounding context.

python
req = Request(url, data=body, headers=headers, method=method)

    try:
        with urlopen(req, timeout=30) as resp:
            resp_data = resp.read().decode("utf-8")
            return json.loads(resp_data) if resp_data else {}
    except HTTPError as e:

Tainted flow: 'req' from os.environ.get (line 290, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The WeChat token request embeds the app secret directly in the URL query string. Secrets in URLs are commonly exposed via logs, proxies, monitoring systems, crash reports, and intermediary infrastructure, making credential leakage more likely even when using HTTPS.

Content

Scanner excerpt · scripts/publisher.py (reported line 291)May include surrounding context.

python
f"grant_type=client_credential&appid={appid}&secret={secret}"
        )
        req = Request(token_url, method="GET")
        with urlopen(req, timeout=30) as resp:
            token_data = json.loads(resp.read().decode("utf-8"))

        if "access_token" not in token_data:

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

This mismatch is security-relevant because the manifest frames the skill as content creation/optimization, while the body also authorizes real external publishing, scheduling, file-system writes, and credential-backed network operations. Hidden or under-declared side effects increase the chance that a user or orchestrator invokes the skill without realizing it can perform irreversible actions on external accounts or local content stores.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

This mismatch is security-relevant because the manifest frames the skill as content creation/optimization, while the body also authorizes real external publishing, scheduling, file-system writes, and credential-backed network operations. Hidden or under-declared side effects increase the chance that a user or orchestrator invokes the skill without realizing it can perform irreversible actions on external accounts or local content stores.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding

This mismatch is security-relevant because the manifest frames the skill as content creation/optimization, while the body also authorizes real external publishing, scheduling, file-system writes, and credential-backed network operations. Hidden or under-declared side effects increase the chance that a user or orchestrator invokes the skill without realizing it can perform irreversible actions on external accounts or local content stores.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
89% confidence
Finding

The skill is explicitly designed to use high-value platform credentials from environment variables and then perform network actions on external accounts. Credential handling is expected in this context, but it still materially increases risk because any overbroad prompt trigger, missing permission scope, logging mistake, or unsafe script behavior could result in token misuse, unauthorized posting, or credential exposure.

Content

Scanner excerpt · SKILL.md (reported line 30)May include surrounding context.

md
| 变量 | 必需 | 说明 |
|------|------|------|
| `CE_TWITTER_BEARER_TOKEN` | 否 | Twitter API v2 Bearer Token |
| `CE_LINKEDIN_ACCESS_TOKEN` | 否 | LinkedIn API Access Token |
| `CE_WECHAT_APPID` | 否 | 微信公众号 AppID |
| `CE_WECHAT_SECRET` | 否 | 微信公众号 AppSecret |
| `CE_MEDIUM_TOKEN` | 否 | Medium Integration Token |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 232)May include surrounding context.

md
python3 scripts/obsidian_sync.py --action connect --data '{"vault_path":"~/MyVault"}'

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 235)May include surrounding context.

md
python3 scripts/obsidian_sync.py --action connect --data '{"vault_path":"~/MyVault"}'

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 238)May include surrounding context.

md
python3 scripts/obsidian_sync.py --action connect --data '{"vault_path":"~/MyVault"}'

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 241)May include surrounding context.

md
python3 scripts/obsidian_sync.py --action connect --data '{"vault_path":"~/MyVault"}'

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 244)May include surrounding context.

md
python3 scripts/obsidian_sync.py --action connect --data '{"vault_path":"~/MyVault"}'

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The import path is constructed from user-controlled file_rel via os.path.join(vault_path, file_rel) and then opened directly, with no canonicalization or containment check. This allows path traversal to read files outside the Obsidian vault, potentially exposing sensitive local files if an attacker can influence the import request.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The export path is built with os.path.join(vault_path, file_rel) and then written without validating that the resolved path stays inside the configured vault. An attacker or untrusted caller can supply traversal segments such as ../ to overwrite arbitrary files accessible to the process, which is especially dangerous because this function creates parent directories and writes attacker-controlled content.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/publisher.py (reported line 195)May include surrounding context.

python
def _publish_linkedin(adapted: Dict[str, Any]) -> Dict[str, Any]:
    """发布到 LinkedIn。

    使用 LinkedIn API,通过 Access Token 认证。

    Args:
        adapted: 已适配的 LinkedIn 内容。

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill describes capabilities that read environment variables, access local files, write blog/markdown output, and publish over external networks, but it declares no explicit tool scope or permission boundary. In an agent environment, this creates overbroad authority and makes accidental or unauthorized file access, token-backed publishing, or network actions harder to constrain and audit.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

该行明确写明“你始终使用中文与用户沟通”,这构成了固定语言策略,并未说明用户可选择其他语言或进行语言偏好设置。根据规则,强制特定语言而无用户 opt-in 属于自然语言政策违规。

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

Broad trigger phrases for content management can cause the skill to activate on ordinary conversation and begin creating, updating, deleting, importing, or exporting content unexpectedly. In a skill with file-write and publish pathways, overbroad activation meaningfully increases the risk of unintended state changes or data handling.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

Ambiguous publish/adapt triggers are dangerous because this skill can perform real network-backed publication and scheduling using stored credentials. A fuzzy match on phrases like publishing or distribution could lead to unintended previews, adaptations, or even posting workflows against external platforms.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

“查看数据”“内容表现”“指标报告”均是较常见表达,再加上“或类似意图”使触发范围进一步扩大。文档未说明这些短语必须关联到该技能支持的平台或内容对象,容易产生误触发。

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

该处以行为准则形式要求“始终使用中文与用户沟通”,再次固化了单一语言要求。文档没有提供切换语言的例外条件、用户选择入口或区域合规说明。

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This markdown file instructs users to set access tokens, app secrets, and vault paths, but it does not include any warning about protecting those credentials, avoiding shell history leakage, or limiting exposure of sensitive environment variables. Because the skill interacts with external publishing platforms and sensitive auth data, the description should explicitly warn users about credential sensitivity and safe storage.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This markdown file presents all operational guidance in Chinese, including headings and best-practice instructions, but does not indicate that the user can select another language or that the skill is intentionally limited to a Chinese-speaking audience. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.