T09 · Insecure Skill Coding Practices
- Location
setup.py:284- Finding
API Credentials Stored Without Restrictive File Permissions
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This is a coherent design-news tool, but it needs review because it stores API keys in a local plaintext file and uses unsafe shell command construction during setup.
Install only if you are comfortable providing Serper and DeepSeek API keys, having them stored in a local .env file, and sending search-result material plus your configured design preferences to external APIs. Prefer setting API keys through your environment instead of running the setup wizard, restrict .env permissions if you use it, avoid install paths with spaces or shell metacharacters, and review any cron entry before copying it.
setup.py:284API Credentials Stored Without Restrictive File Permissions
setup.py:541Shell Command Injection Through Unquoted Interpreter and Script Paths
setup.py:396Unsafe Path Interpolation in Suggested Crontab Entry
brief.py:68Indirect Prompt Injection Through Untrusted Search Results
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
if not DEEPSEEK_KEY:
raise EnvironmentError("DEEPSEEK_API_KEY 未设置,请检查环境变量")
resp = requests.post(
DEEPSEEK_URL,
headers={
"Authorization": f"Bearer {DEEPSEEK_KEY}",
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
if tbs:
payload["tbs"] = tbs
resp = requests.post(
SERPER_URL,
headers={"X-API-KEY": SERPER_KEY, "Content-Type": "application/json"},
json=payload,
The documented purpose is a design-news brief, but the described behavior includes collecting and validating API keys, writing secrets to local files, generating cron instructions, and executing another program. This mismatch is dangerous because users may consent to a harmless content skill while unintentionally granting credential handling, persistence-related setup, and code execution capabilities that exceed the stated purpose.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# ── 自动加载 .env(setup.py 写入的 key)─────────────────────────────────────
def load_dotenv():
env_path = BASE_DIR / ".env"
if env_path.exists():
import os
for line in env_path.read_text(encoding="utf-8").splitlines():
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
"""
setup.py
Design Brief Skill 一键配置向导
运行后自动生成 .env 和 config.yaml
"""
import os
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
"""
setup.py
Design Brief Skill 一键配置向导
运行后自动生成 .env 和 config.yaml
"""
import os
The script writes API credentials directly to a local .env file in plaintext. In a developer-tooling context this is common, but it remains dangerous because secrets may be exposed through weak filesystem permissions, accidental commits, shared machines, or backup/sync systems.
# ── 写入文件 ──────────────────────────────────────────────────────────────────
def write_env(serper_key: str, deepseek_key: str):
env_path = BASE_DIR / ".env"
env_path.write_text(
f"SERPER_API_KEY={serper_key}\n"
f"DEEPSEEK_API_KEY={deepseek_key}\n",
os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.
os.environ["SERPER_API_KEY"] = serper_key
os.environ["DEEPSEEK_API_KEY"] = deepseek_key
os.chdir(BASE_DIR)
os.system(f"{sys.executable} {BASE_DIR / 'run.py'}")
if __name__ == "__main__":
The skill declares required environment variables, package installation, file editing, scheduled execution, and direct command invocation, but it does not define any explicit tool permissions or allowed-tool scope. In an agent environment, this over-broad implicit capability can let the skill access network, shell, and local files beyond what users reasonably expect, increasing the blast radius if the implementation is changed or abused.
The natural-language instructions, setup steps, and examples are presented only in Chinese, which can force a specific language experience on users who invoke the skill via English triggers such as 'design brief' or 'design news today'. The file does not state that the skill is Chinese-only, region-specific, or provide any language-selection option.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
import requests
DEEPSEEK_KEY = os.environ.get("DEEPSEEK_API_KEY", "")
DEEPSEEK_URL = "https://api.deepseek.com/chat/completions"
DEEPSEEK_MODEL = "deepseek-chat"
# ── System Prompt ─────────────────────────────────────────────────────────────
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
import requests
DEEPSEEK_KEY = os.environ.get("DEEPSEEK_API_KEY", "")
DEEPSEEK_URL = "https://api.deepseek.com/chat/completions"
DEEPSEEK_MODEL = "deepseek-chat"
# ── System Prompt ─────────────────────────────────────────────────────────────
This function sends aggregated prompt content to an external API, which is a real security-relevant data flow in an agent skill context. Because the prompt is built from raw source material and favorite_designer metadata, the skill can exfiltrate sensitive or internal content to a third party if upstream inputs are not trusted or filtered.
if not DEEPSEEK_KEY:
raise EnvironmentError("DEEPSEEK_API_KEY 未设置,请检查环境变量")
resp = requests.post(
DEEPSEEK_URL,
headers={
"Authorization": f"Bearer {DEEPSEEK_KEY}",
The code transmits raw_items content, titles, snippets, URLs, and possibly user preference data to a third-party LLM service without any consent gate, redaction step, or documented data handling control. If the upstream content or user-associated metadata contains confidential, proprietary, or personal information, this creates an external data exposure risk.
This manifest/config file contains user-facing instructions and field guidance almost exclusively in Chinese, which effectively forces a specific language for users configuring the skill. There is no visible opt-in, alternative language option, or justification that the skill is intended only for a Chinese-speaking region or audience.
The request payload hard-codes "hl": "en" and "gl": "us", which imposes a specific language and geographic locale on every search. This is a natural-language policy concern because the file contains no opt-in, fallback, or justification for forcing English/US results despite supporting Chinese-language roles and queries elsewhere in the skill.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
if tbs:
payload["tbs"] = tbs
resp = requests.post(
SERPER_URL,
headers={"X-API-KEY": SERPER_KEY, "Content-Type": "application/json"},
json=payload,
The module docstring and user-facing usage text are entirely in Chinese, and later log/output strings throughout the file are also Chinese-only. This imposes a specific language on users without any opt-in or indication that the skill is intentionally limited to a Chinese-speaking or region-specific context.
The script announces that it will generate a .env file, but it does not provide an explicit security warning about storing live API credentials in plaintext on disk. Local plaintext secret storage increases exposure if the workstation, repository, backups, or file permissions are not properly controlled.
User-facing prompts, descriptions, and guidance throughout the script are written in Chinese, and the title/brief do not indicate that the skill is Chinese-only or provide any opt-in language selection. This creates a language-policy concern because the locale is effectively forced without explicit user choice.
The wizard asks users to paste API keys, then immediately transmits those secrets to third-party services for validation without a clear, explicit privacy warning at the time of collection. While the destinations are the intended vendors, users should be informed that their entered credentials will be sent over the network for verification.
This duplicate finding describes the same secret-bearing Serper validation request. In context, the risk is not malicious exfiltration but undisclosed outbound handling of credentials.
def verify_serper_key(key: str) -> bool:
try:
resp = requests.post(
"https://google.serper.dev/search",
headers={"X-API-KEY": key, "Content-Type": "application/json"},
json={"q": "design news", "num": 1},
This duplicate finding describes the same secret-bearing Serper validation request. In context, the risk is not malicious exfiltration but undisclosed outbound handling of credentials.
def verify_serper_key(key: str) -> bool:
try:
resp = requests.post(
"https://google.serper.dev/search",
headers={"X-API-KEY": key, "Content-Type": "application/json"},
json={"q": "design news", "num": 1},
This duplicate finding refers to the same DeepSeek credential validation request. The concern remains that a sensitive token is transmitted externally without especially prominent disclosure at entry time.
def verify_deepseek_key(key: str) -> bool:
try:
resp = requests.post(
"https://api.deepseek.com/chat/completions",
headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json"},
json={
This duplicate finding refers to the same DeepSeek credential validation request. The concern remains that a sensitive token is transmitted externally without especially prominent disclosure at entry time.
def verify_deepseek_key(key: str) -> bool:
try:
resp = requests.post(
"https://api.deepseek.com/chat/completions",
headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json"},
json={
No suspicious patterns detected.