Back to skill

Security audit

Rn Skills

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a Xiaohongshu content generator, but its image helper uses unsafe shell commands, reads a shell profile for credentials, can make many paid API calls, and writes to hard-coded local paths.

Review carefully before installing. Only use this after replacing shell-based curl/cp calls with safe Python APIs, removing ~/.zshrc credential parsing, enforcing explicit confirmation and job limits for paid API calls, and changing output paths to a user-approved project-local directory.

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
generate_images.py:64
Finding
Shell Command Injection Through Unvalidated Credentials and API Response Values<![CDATA[ ## Vulnerability Details **File Location**: `generate_images.py:64-91` **Vulnerability Type**: OS command injection through `shell=True` and string interpolation **Risk Level**: High ### Vulnerable Code ```python def submit_task(prompt): cmd = f'''curl -s "https://dashscope.aliyuncs.com/api/v1/services/aigc/text2image/image-synthesis" \\ -H "Content-Type: application/json" \\ -H "Authorization: Bearer {API_KEY}" \\ -H "X-DashScope-Async: enable" \\ -d '{{ "model": "wanx2.1-t2i-turbo", "input": {{ "prompt": "{prompt}" }}, "parameters": {{ "size": "720*1280", "n": 1 }} }}' ''' result = subprocess.run(cmd, shell=True, capture_output=True, text=True) return result.stdout def get_task_status(task_id): cmd = f'''curl -s "https://dashscope.aliyuncs.com/api/v1/tasks/{task_id}" \\ -H "Authorization: Bearer {API_KEY}" ''' result = subprocess.run(cmd, shell=True, capture_output=True, text=True) return result.stdout def download_image(url, output_path): cmd = f'curl -s "{url}" -o "{output_path}"' subprocess.run(cmd, shell=True) ``` ### Technical Analysis The script constructs shell command strings by directly interpolating multiple values and then executes those strings with `subprocess.run(..., shell=True)`. The affected values include: - `API_KEY`, obtained from an environment variable or `~/.zshrc`. - `task_id`, obtained from the remote DashScope API response. - `url`, obtained from the remote DashScope API response. - `output_path`, currently derived from hard-coded paths but still passed through a shell. Shell quoting does not make these values safe. An interpolated value containing a double quote followed by shell metacharacters can terminate the intended quoted argument and inject an additional command. The same issue affects both the authorization header and values returned by the remote service. The JSON request is also assembled manually inside shell quoting. Alth ...[truncated 2083 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Eliminate `shell=True` entirely. 2. Prefer the official DashScope SDK or a Python HTTP client such as `urllib.request` or a properly pinned `requests` dependency. 3. If `curl` must be retained, pass an argument array without invoking a shell: ```python result = subprocess.run( [ "curl", "-sS", "https://dashscope.aliyuncs.com/api/v1/services/aigc/text2image/image-synthesis", "-H", "Content-Type: application/json", "-H", f"Authorization: Bearer {API_KEY}", "-H", "X-DashScope-Async: enable", "--data-binary", json.dumps(payload), ], shell=False, capture_output=True, text=True, check=True, ) ``` 4. Build request bodies with `json.dumps()` rather than manual shell quoting. 5. Validate remote task IDs against the exact format documented by DashScope before using them. 6. Parse and validate download URLs with `urllib.parse`; require HTTPS and an expected host or documented host allowlist. 7. Use Python file operations such as `shutil.copy2()` instead of invoking `cp`. 8. Read credentials only from a dedicated secret provider or the process environment. Do not parse interactive shell startup files. 9. Add explicit timeouts, return-code checks, and safe error handling without printing credentials or full authorization headers. 10. Add regression tests containing quotes, semicolons, command substitutions, newlines, and other shell metacharacters to verify that values are always treated as data. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
generate_images.py:94
Finding
Unbounded and Excessive Billable API Operations on Script Invocation<![CDATA[ ## Vulnerability Details **File Location**: `generate_images.py:94-152` **Vulnerability Type**: Uncontrolled resource consumption and billable external side effects **Risk Level**: Medium ### Vulnerable Code ```python # 存储所有任务 ID 和对应的输出路径 all_tasks = [] # 春日空气感叠穿 print("\n🌿 [1/4] 春日空气感叠穿...") airy_dir = "/Users/yk/Documents/work/skills/rn-skills/output/2026-03-15_春日空气感叠穿" for i, prompt in enumerate(prompts_airy): print(f" 提交任务 {i+1}/6...") response = submit_task(prompt) try: resp_json = json.loads(response) task_id = resp_json.get('output', {}).get('task_id') if task_id: all_tasks.append((task_id, f"{airy_dir}/page_{i}.png", f"春日空气感_{i}")) print(f" ✓ 任务 ID: {task_id}") except: print(f" ✗ 提交失败:{response}") # 新中式茶道穿搭 print("\n🍵 [2/4] 新中式茶道春日穿搭...") tea_dir = "/Users/yk/Documents/work/skills/rn-skills/output/2026-03-15_新中式茶道春日穿搭" for i, prompt in enumerate(prompts_tea): print(f" 提交任务 {i+1}/6...") response = submit_task(prompt) try: resp_json = json.loads(response) task_id = resp_json.get('output', {}).get('task_id') if task_id: all_tasks.append((task_id, f"{tea_dir}/page_{i}.png", f"新中式_{i}")) print(f" ✓ 任务 ID: {task_id}") except: print(f" ✗ 提交失败:{response}") # Clean Fit 极简通勤 print("\n💼 [3/4] Clean Fit 极简通勤...") clean_dir = "/Users/yk/Documents/work/skills/rn-skills/output/2026-03-23_Clean Fit 极简通勤" for i, prompt in enumerate(prompts_clean): print(f" 提交任务 {i+1}/6...") response = submit_task(prompt) try: resp_json = json.loads(response) task_id = resp_json.get('output', {}).get('task_id') if task_id: all_tasks.append((task_id, f"{clean_dir}/page_{i}.png", f"CleanFit_{i}")) print(f" ✓ 任务 ID: {task_id}") except: print(f" ✗ 提交失败:{response}") # 复古港风约会穿搭 print("\n🌹 [4/4] 复古港风约会穿搭...") retro_dir = "/Users/yk/Documents/work/skills/r ...[truncated 2387 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Move all executable workflow logic into a `main()` function and protect it with: ```python if __name__ == "__main__": main() ``` 2. Enforce the documented maximum of 12 images in code, not only in instructions. 3. Require explicit selection of topics and image counts through validated command-line arguments or configuration. 4. Display the number of planned jobs and estimated cost before submission, and require affirmative confirmation unless an explicit non-interactive flag is supplied. 5. Add a dry-run mode that performs no external requests. 6. Add per-run and daily budget or quota controls where the provider supports them. 7. Configure connection and total request timeouts. 8. Implement bounded retries with exponential backoff and rate-limit handling. 9. Record unique run identifiers and use idempotency protections where possible to avoid duplicate submissions. 10. Add tests asserting that the total submitted image count cannot exceed the configured maximum. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
generate_images.py:102
Finding
Hard-Coded Absolute Output Paths Permit Writes Outside the Project Boundary<![CDATA[ ## Vulnerability Details **File Location**: `generate_images.py:102-152` **Vulnerability Type**: Unsafe fixed filesystem destinations and unintended file overwrite **Risk Level**: Medium ### Vulnerable Code ```python airy_dir = "/Users/yk/Documents/work/skills/rn-skills/output/2026-03-15_春日空气感叠穿" for i, prompt in enumerate(prompts_airy): response = submit_task(prompt) try: resp_json = json.loads(response) task_id = resp_json.get('output', {}).get('task_id') if task_id: all_tasks.append((task_id, f"{airy_dir}/page_{i}.png", f"春日空气感_{i}")) except: print(f" ✗ 提交失败:{response}") tea_dir = "/Users/yk/Documents/work/skills/rn-skills/output/2026-03-15_新中式茶道春日穿搭" for i, prompt in enumerate(prompts_tea): response = submit_task(prompt) try: resp_json = json.loads(response) task_id = resp_json.get('output', {}).get('task_id') if task_id: all_tasks.append((task_id, f"{tea_dir}/page_{i}.png", f"新中式_{i}")) except: print(f" ✗ 提交失败:{response}") clean_dir = "/Users/yk/Documents/work/skills/rn-skills/output/2026-03-23_Clean Fit 极简通勤" for i, prompt in enumerate(prompts_clean): response = submit_task(prompt) try: resp_json = json.loads(response) task_id = resp_json.get('output', {}).get('task_id') if task_id: all_tasks.append((task_id, f"{clean_dir}/page_{i}.png", f"CleanFit_{i}")) except: print(f" ✗ 提交失败:{response}") retro_dir = "/Users/yk/Documents/work/skills/rn-skills/output/2026-03-23_复古港风约会穿搭" for i, prompt in enumerate(prompts_retro): response = submit_task(prompt) try: resp_json = json.loads(response) task_id = resp_json.get('output', {}).get('task_id') if task_id: all_tasks.append((task_id, f"{retro_dir}/page_{i}.png", f"港风_{i}")) except: print(f" ✗ 提交失败:{response}") ``` The files are later written through: ```python def download_ima ...[truncated 2304 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Resolve output paths relative to the project root or an explicitly configured output root: ```python PROJECT_ROOT = Path(__file__).resolve().parent OUTPUT_ROOT = (PROJECT_ROOT / "output").resolve() ``` 2. Reject any destination whose resolved path is not a descendant of `OUTPUT_ROOT`. 3. Create a unique run directory using a sanitized topic identifier and timestamp. 4. Use `pathlib.Path` and native Python file operations instead of shell commands. 5. Open new outputs in exclusive mode where replacement is not intended, or require an explicit `--overwrite` option. 6. Check for symbolic links and unexpected file types before writing sensitive destinations. 7. Create directories with deliberate permissions and handle failures before submitting billable remote jobs. 8. Write downloads to a temporary file in the same approved directory, validate the response status and media type, then atomically rename the file. 9. Remove developer-specific absolute paths from the repository. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (34)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
There is a material mismatch between the stated purpose and the behavior described by analysis: the skill is presented as a full Xiaohongshu creation workflow, but the behavior includes undeclared local environment access, fixed local output handling, and failure to implement several promised steps. Misrepresentation is dangerous because users may authorize execution expecting only content creation, while the skill may access secrets or modify the local system in ways that were not transparently disclosed.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
}}
  }}'
'''
    result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
    return result.stdout

def get_task_status(task_id):
Confidence
97% confidence
Finding
This is the strongest concrete issue in the file: a tool capable of arbitrary shell execution is fed a command string assembled from prompt content and credentials. In the context of a content-creation skill, prompts may eventually be user-influenced, making this an especially dangerous path to local command execution.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
cmd = f'''curl -s "https://dashscope.aliyuncs.com/api/v1/tasks/{task_id}" \\
  -H "Authorization: Bearer {API_KEY}"
'''
    result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
    return result.stdout

def download_image(url, output_path):
Confidence
90% confidence
Finding
A shell-capable tool is invoked with a command string containing a remote task identifier and credentials. Even though the endpoint is fixed, remote-controlled task_id content could abuse the shell boundary, turning a status check into a command execution sink.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
def download_image(url, output_path):
    cmd = f'curl -s "{url}" -o "{output_path}"'
    subprocess.run(cmd, shell=True)

print("=" * 60)
print("开始生成穿搭图片...")
Confidence
95% confidence
Finding
The download step combines remote URL data with shell execution, creating a high-risk tool-parameter abuse path. If the upstream service or response is tampered with, this can execute arbitrary commands or write attacker-chosen content to local files.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
page0 = f"{dir_path}/page_0.png"
    cover = f"{dir_path}/cover.png"
    if os.path.exists(page0) and not os.path.exists(cover):
        subprocess.run(f'cp "{page0}" "{cover}"', shell=True)
        print(f"  ✓ {cover}")

print("\n✅ 全部完成!")
Confidence
90% confidence
Finding
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).

Description-Behavior Mismatch

Medium
Confidence
88% confidence
Finding
The manifest frames this as a Xiaohongshu creator and explicitly says it is not for other platforms such as Douyin/Weibo. However, the README's feature list says the skill auto-collects trends from Weibo, Douyin, Vogue, Pinterest, and Instagram, expanding behavior beyond the manifest's stated platform scope.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill documents capabilities that require shell execution, file reads, and environment variable access, but it does not declare any tool scope or permissions. This weakens least-privilege controls and can cause the agent to access local files or secrets more broadly than users would reasonably expect.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
The skill is written to operate entirely in Chinese and the invocation/output examples assume Chinese-language use, but there is no statement that this is optional or limited to a justified region-specific deployment. Under the policy, forcing a specific language without opt-in is a natural-language policy concern.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The activation phrases are broad enough that normal conversation such as '创作小红书' or '收集素材' could trigger a workflow with network access, local file writes, and external API calls. Ambiguous triggering is risky because it can cause unintended execution of side-effectful actions without sufficiently explicit user consent.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The manifest says the skill is not for other platforms, yet the workflow explicitly gathers input from Weibo, Douyin, Pinterest, and Instagram. This inconsistency can mislead users about where data comes from and increases legal, policy, and privacy risk when third-party platform content is collected or repurposed without clear disclosure.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The when-to-run section uses ambiguous examples that do not clearly separate harmless drafting from side-effectful automation. Because this skill can perform web searches, generate images through a paid API, and write local files, unclear scope boundaries can lead to accidental over-execution and unexpected cost or data exposure.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The workflow sends prompts and content to a third-party image-generation API using an API key, but the skill does not warn that user content will leave the local environment. This creates privacy, confidentiality, and billing risk, especially if prompts contain proprietary, personal, or unpublished material.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill creates output directories, saves files, and updates history.json, but it does not prominently warn users that local files will be modified. Undisclosed file creation and state mutation are dangerous because they can overwrite data, leak sensitive content into predictable locations, or leave persistent artifacts the user did not expect.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The description states the image generation setup supports Chinese prompts better, and the rest of the configuration is oriented around Chinese-specific aesthetics and platform guidance. In a manifest/config context, this can amount to a locale preference baked into the skill without any visible user opt-in or alternative language/locale path.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The manifest limits the skill to 小红书 content creation and explicitly says it is not for other platforms such as 抖音/微博. However, this configuration defines search keyword templates for 微博热点 and 抖音热点, indicating the skill is designed to gather trend material from those platforms as part of its behavior.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
The skill reads the user's ~/.zshrc to recover an API key if the environment variable is absent. Accessing a broad shell profile file is over-privileged for an image helper, risks exposing unrelated secrets/configuration, and normalizes secret scraping from user dotfiles without explicit consent.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
All user-facing comments and runtime status/error messages are in Chinese, with no indication that the user can choose another language. This can violate language or locale policy where skills must not force a specific language without user opt-in or documented justification.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This code accesses the DASHSCOPE_API_KEY credential from the process environment and, if missing, falls back to reading ~/.zshrc directly. Although the script prints an error when the key is absent, it does not disclose to the user that it will inspect a shell profile file to obtain a secret.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The file uses shell commands for networking and file operations where direct library calls would suffice. In a content-generation skill, introducing generic shell execution substantially increases risk because any future prompt, URL, or response handling bug can become arbitrary command execution on the user's machine.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The script submits image-generation prompts to a remote DashScope API and includes a bearer token in outbound HTTP requests, then downloads generated images from returned URLs. While progress messages are printed, there is no user-facing disclosure that prompt content and authentication data are being sent to external services.

External Transmission

Medium
Category
Data Exfiltration
Content
]

def submit_task(prompt):
    cmd = f'''curl -s "https://dashscope.aliyuncs.com/api/v1/services/aigc/text2image/image-synthesis" \\
  -H "Content-Type: application/json" \\
  -H "Authorization: Bearer {API_KEY}" \\
  -H "X-DashScope-Async: enable" \\
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
}}
  }}'
'''
    result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
    return result.stdout

def get_task_status(task_id):
Confidence
97% confidence
Finding
The script builds a shell command string containing both a secret-bearing API key and unescaped prompt text, then executes it with shell=True. Because prompt content is interpolated directly into a single-quoted curl payload, any quote-breaking or shell metacharacters in prompt data could trigger command injection and arbitrary local command execution.

Tainted flow: 'cmd' from os.environ.get (line 83, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Content
}}
  }}'
'''
    result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
    return result.stdout

def get_task_status(task_id):
Confidence
91% confidence
Finding
A secret from the environment or ~/.zshrc is inserted into a shell command and executed. Even if the API key itself is not attacker-controlled, combining credential material with shell execution increases the blast radius of injection bugs and exposes the key to process-level leakage and command-line capture.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
cmd = f'''curl -s "https://dashscope.aliyuncs.com/api/v1/tasks/{task_id}" \\
  -H "Authorization: Bearer {API_KEY}"
'''
    result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
    return result.stdout

def download_image(url, output_path):
Confidence
84% confidence
Finding
This call executes a shell command assembled with an API key and task_id. In the current flow task_id originates from a remote service response, so a malformed or attacker-controlled value could break command boundaries and execute unintended shell commands on the host.

Tainted flow: 'cmd' from os.environ.get (line 83, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Content
cmd = f'''curl -s "https://dashscope.aliyuncs.com/api/v1/tasks/{task_id}" \\
  -H "Authorization: Bearer {API_KEY}"
'''
    result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
    return result.stdout

def download_image(url, output_path):
Confidence
83% confidence
Finding
The command executed here includes secret-derived data and a remotely sourced task identifier. This creates an unsafe tainted flow from credentials and network-controlled data into shell execution, which can enable command injection or unintended disclosure in logs and process listings.

Static analysis

No suspicious patterns detected.