T09 · Insecure Skill Coding Practices
- Location
scripts/search.sh:130- Finding
Tavily API Key Exposed Through Process Command-Line Arguments
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill is mostly a web-search reporting tool, but its scripts search and report on occupational annuity despite promising not to, and they handle Tavily API credentials in a way users should review.
Review before installing. Use it only if you are comfortable sending searched organization names and pension-related queries to Tavily and public search engines. Do not rely on the claimed enterprise-annuity-only scope unless the scripts are fixed to remove 职业年金 queries and report fields. Use a limited Tavily key, avoid shared machines, and prefer safer secret handling before running batch searches.
scripts/search.sh:130Tavily API Key Exposed Through Process Command-Line Arguments
scripts/batch_search.sh:59Batch Search Exposes Tavily API Key Through Process Command-Line Arguments
The description and the code are not aligned. The most significant mismatch is scope: the description explicitly limits the skill to enterprise annuity and says it does not query occupational annuity, but the code repeatedly searches for both “企业年金/职业年金”, labels reports as “企业年金/职业年金调查报告”, and infers “职业年金” for public institutions. The script also has a materially different operational mode: it is a batch processor over a fixed company list, generating markdown files and a summary report, rather than a general-purpose lookup skill. Additionally, it infers unit nature, pension type, and confidence from search snippets using heuristics, which is broader than merely searching and confirming enterprise annuity info. Finally, although permissions are declared as empty, the code depends on external network access to Tavily and web search URLs. Overall, this is a clear description-behavior mismatch.
核心不匹配点在于描述明确限制“只查询企业年金,不查询职业年金”,但代码多处直接把“职业年金”作为查询目标和报告内容的一部分:关键词包含“企业年金 职业年金”,报告标题为“企业年金/职业年金调查报告”,核心结论中也有“年金类型”判断,因此实际行为明显超出并违背了声明范围。此外,代码还系统性查询单位性质、招聘福利、预算/决算、上级单位等,用于辅助判断年金情况,这比声明的“搜索并确认企业年金信息”更宽。输出标准化报告、来源链接、错误检查这些与描述基本一致,但不足以抵消对“职业年金”限制的直接违反,因此应判定为描述与行为存在实质性不匹配。
The skill metadata explicitly says it should only query 企业年金 and not 职业年金, but the search keywords intentionally include 职业年金 and the downstream logic uses those results to make conclusions. This causes scope violation and can lead to collecting, inferring, and reporting on categories of information the user did not authorize, increasing the chance of inaccurate or overbroad investigations.
The generated report title and conclusion sections expand the skill from enterprise-pension lookup into combined enterprise/occupational-pension investigation. Even if the underlying search were narrowed later, the reporting layer still instructs the agent to present out-of-scope determinations, which can mislead users and institutionalize unauthorized analysis.
The generated report template explicitly covers both 企业年金 and 职业年金 despite the skill metadata stating it should only query 企业年金. This creates a scope-integrity problem: users may unknowingly receive or act on out-of-scope results, increasing the chance of incorrect investigations and unintended collection or disclosure of information about a different pension category.
The script actively searches for '职业年金' in a keyword set even though the skill description says it only searches 企业年金. In a compliance-sensitive research skill, this mismatch can cause unauthorized or misleading data collection and materially undermines user expectations about what external queries will be sent.
The skill mandates use of shell-capable behaviors such as PDF parsing with local tools like pypdf/strings/pdftotext, but it declares no explicit tool scope or permissions. That creates an authorization and transparency gap: a host may expose broader execution capability than users expect, and the skill can trigger command execution paths without clear sandboxing or least-privilege constraints.
The skill name and full operating instructions are written as a Chinese-only workflow for producing reports, with no indication that the user may choose another language. This creates a natural-language locale constraint without opt-in or an explicit justification that the skill is intended only for a Chinese-language/regional compliance context.
The skill requires sending the target organization name and related search terms to Tavily and multiple public search engines, but it gives no user warning that their query subject will be disclosed to third-party services. This creates a privacy and data-governance risk, especially when the searched entity is sensitive, nonpublic, or user-identifying in a compliance or due-diligence context.
The manifest states the skill is limited to enterprise annuity queries, but the trigger phrases expand into broader domains such as general benefits inquiries and organization-type investigation. This creates scope drift: the agent may activate on requests outside its declared purpose, causing unintended data collection, misleading outputs, or use in contexts the user did not intend.
Overly broad triggers increase the chance that this skill will be invoked for common requests unrelated to enterprise pension lookup. In a search-and-investigation skill, accidental activation is risky because it can steer users into irrelevant or privacy-sensitive investigative workflows and produce authoritative-looking reports on the wrong topic.
The script's natural-language interface, comments, logs, and generated report text are entirely in Chinese, which effectively fixes the skill's user-facing language. There is no indication that users may select another language or locale, so this may violate a language-choice policy.
The script performs external network transmission to Tavily, including organization-specific search terms and an API credential in the request body. In an investigative skill, this is security-relevant because it transfers target information and user research intent to a third party, which may create privacy, compliance, or operational exposure if not disclosed and controlled.
return 1
fi
curl -s -X POST https://api.tavily.com/search \
-H "Content-Type: application/json" \
-d "{
\"api_key\": \"$TAVILY_API_KEY\",
The script performs external network transmission to Tavily, including organization-specific search terms and an API credential in the request body. In an investigative skill, this is security-relevant because it transfers target information and user research intent to a third party, which may create privacy, compliance, or operational exposure if not disclosed and controlled.
return 1
fi
curl -s -X POST https://api.tavily.com/search \
-H "Content-Type: application/json" \
-d "{
\"api_key\": \"$TAVILY_API_KEY\",
The script sends organization names and related investigative queries to a third-party search API, but there is no explicit user-facing disclosure or consent mechanism. In this skill context, the queried entities are investigation targets, so external transmission can expose user work product, research intent, or sensitive organizational inquiry patterns to an outside service.
The script claims to perform enterprise-pension investigation, but the keyword set explicitly includes occupational-pension terms. This mismatch is dangerous because operators may trust the documented behavior while the actual implementation performs broader collection and analysis than advertised.
Comments, usage text, status messages, warnings, and report content are all presented in Chinese, which effectively forces a specific language for interaction. Under the policy rule, a fixed language/locale without user opt-in or an explicit documented regional justification is a natural-language policy concern.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
if [ ${#missing[@]} -ne 0 ]; then
echo -e "${RED}错误:缺少依赖 ${missing[*]}${NC}"
echo "请运行:sudo apt-get install curl jq"
exit 1
fi
}
The script sends search queries containing the target company name to an external API endpoint. External transmission is expected for a search skill, but it is still a security-relevant issue here because the data flow is not prominently disclosed and may reveal sensitive research intent to a third party.
print_info "Tavily 搜索:'$query' (最多 $max_results 条结果)"
local response
response=$(curl -s -X POST https://api.tavily.com/search \
-H "Content-Type: application/json" \
-d "{
\"api_key\": \"$api_key\",
The script sends search queries containing the target company name to an external API endpoint. External transmission is expected for a search skill, but it is still a security-relevant issue here because the data flow is not prominently disclosed and may reveal sensitive research intent to a third party.
print_info "Tavily 搜索:'$query' (最多 $max_results 条结果)"
local response
response=$(curl -s -X POST https://api.tavily.com/search \
-H "Content-Type: application/json" \
-d "{
\"api_key\": \"$api_key\",
User-provided company names are embedded into search queries and transmitted to Tavily without an explicit user-facing warning at the moment of network use. In this skill context, company names may be commercially sensitive investigation targets, so silent transmission to a third-party service creates a real privacy and confidentiality risk.
Natural-language policy review applies to all file types, including manifest metadata. The user-facing description and all trigger phrases are exclusively in Chinese, with no indication that the skill supports user language choice or is intentionally limited to a Chinese-only audience for a documented regional reason.
The script depends on the sensitive environment variable TAVILY_API_KEY and uses it for authenticated external requests. There is no explicit warning in the usage text or runtime messages that a credential is required and will be used for outbound API calls.
The code loads Tavily API keys from ../tavily_keys.txt or the TAVILY_API_KEY environment variable, which is access to sensitive credentials. While there is some operational logging about whether keys were found, there is no explicit disclosure explaining that the script will read and use credentials from local storage or environment variables.
No suspicious patterns detected.