低风险 — 风险评分 28/100
上次扫描:21 小时前 重新扫描
28 /100
snarky-expense-butler
毒舌记账管家,支持记账、查询、预算提醒、毒舌消费分析、地域统计、趋势图
Skill functions as a legitimate local expense tracker but contains undocumented network code (OpenRouter API call) that contradicts the SKILL.md declaration of 'no external dependencies, no API key'. The network functionality degrades gracefully to matplotlib and poses no data exfiltration risk.
技能名称snarky-expense-butler
分析耗时59.3s
引擎pi
可以安装
Update SKILL.md to document the optional OpenRouter API integration for trend chart generation. Remove the credential-reading code for ~/.openclaw/openclaw.json (keychain approach can't extract the key anyway). Declare network:READ if the API feature is retained.

安全发现 3 项

严重性 安全发现 位置
中危
SKILL.md claims no external dependencies but code makes network calls 文档欺骗
SKILL.md explicitly states '纯本地 JSON 存储的个人消费记录系统,无外部依赖,无需 API Key'. However, scripts/expense_trends.py contains a generate_with_llm() function that makes an HTTP POST request to https://openrouter.ai/api/v1/chat/completions. This is a doc-to-code mismatch on a core security-relevant claim.
response = requests.post("https://openrouter.ai/api/v1/chat/completions", headers={"Authorization": f"Bearer {api_key}", ...}, json={...})
→ Update SKILL.md to document that the trend chart feature optionally uses OpenRouter API if OPENROUTER_API_KEY is set, and that matplotlib is used as a fallback.
scripts/expense_trends.py:126
低危
Credential config file access attempt 敏感访问
get_openrouter_api_key() attempts to read ~/.openclaw/openclaw.json to extract an OpenRouter API key. However, the key is stored in the system keychain, not directly accessible from the JSON, so no actual credential is exfiltrated. The attempt is benign but reveals knowledge of credential storage patterns.
config_path = os.path.expanduser('~/.openclaw/openclaw.json')
with open(config_path, 'r') as f:
    config = json.load(f)
→ Remove the ~/.openclaw/openclaw.json reading logic since it cannot extract the key anyway. Rely solely on the OPENROUTER_API_KEY environment variable.
scripts/expense_trends.py:36
低危
SKILL.md budget defaults contradict script hardcoded values 文档欺骗
SKILL.md documents default budget as 日 ¥50/周 ¥350/月 ¥1500, but expense_budget.py hardcodes defaults of ¥500/¥3000/¥12000. This minor inconsistency could mislead users configuring budgets.
daily_budget = budget.get('daily', 500)  # vs SKILL.md default ¥50
→ Align default budget values between SKILL.md and code, or make them configurable via environment variables.
scripts/expense_budget.py:13
资源类型声明权限推断权限状态证据
文件系统 READ WRITE ✓ 一致 All scripts write to ./expense_records.json — declared as data path in SKILL.md
网络访问 NONE READ ✗ 越权 scripts/expense_trends.py:126 — HTTP POST to openrouter.ai
命令执行 NONE NONE No subprocess/os.system calls found
环境变量 NONE READ ✓ 一致 scripts/expense_trends.py:36 — reads OPENROUTER_API_KEY env var
技能调用 NONE NONE N/A
剪贴板 NONE NONE N/A
浏览器 NONE NONE N/A
数据库 NONE NONE N/A
1 项发现
🔗
中危 外部 URL 外部 URL
https://openrouter.ai/api/v1/chat/completions
scripts/expense_trends.py:126

目录结构

8 文件 · 55.9 KB · 1621 行
Python 7f · 1564L Markdown 1f · 57L
├─ 📁 scripts
│ ├─ 🐍 add_expense.py Python 215L · 7.9 KB
│ ├─ 🐍 expense_analysis.py Python 265L · 9.0 KB
│ ├─ 🐍 expense_budget.py Python 178L · 5.8 KB
│ ├─ 🐍 expense_location.py Python 203L · 6.2 KB
│ ├─ 🐍 expense_query.py Python 151L · 5.2 KB
│ ├─ 🐍 expense_report.py Python 280L · 10.6 KB
│ └─ 🐍 expense_trends.py Python 272L · 9.3 KB
└─ 📝 SKILL.md Markdown 57L · 2.0 KB

依赖分析 2 项

包名版本来源已知漏洞备注
requests unpinned import Imported in expense_trends.py but not listed in any dependency file; version not pinned
matplotlib unpinned import Used as fallback for chart generation; not listed in dependency file

安全亮点

✓ No base64-encoded execution, obfuscation, or anti-analysis techniques found
✓ No reverse shell, C2 communication, or data exfiltration to external servers
✓ No access to sensitive paths (~/.ssh, ~/.aws, .env) beyond the benign openclaw config read
✓ No credential harvesting beyond the failed openclaw config read (key is in keychain)
✓ No subprocess/shell execution beyond standard Python file I/O
✓ File locking (fcntl) used for concurrent write safety
✓ Network call gracefully degrades to matplotlib if API fails
✓ No supply chain risks — no external dependencies declared or used
✓ All data stays in local JSON file as documented