T09 · Insecure Skill Coding Practices
- Location
config.json:2- Finding
Exposed API Credential and Insecure Plaintext Credential Storage
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This Tavily search skill is mostly purpose-aligned, but it ships with an exposed API key and stores user-supplied keys in plaintext in the skill directory.
Review before installing. Do not use the bundled Tavily key; it should be revoked by the publisher. If you use this skill, assume searches are sent to Tavily, avoid placing secrets in search queries, and prefer replacing plaintext config.json storage with an environment variable or OS credential manager.
config.json:2Exposed API Credential and Insecure Plaintext Credential Storage
The documented purpose is a search tool, but the skill also describes persistent API key storage and optional retrieval of raw webpage content without clearly surfacing those behaviors in the high-level description. This mismatch weakens informed consent and can expose sensitive credentials or unexpectedly ingest large amounts of untrusted content, which is especially risky in an agent that may further process or store that data.
The skill advertises operational capabilities that imply network access and local configuration persistence, but it does not declare any explicit tool scope or permissions boundary. In an agent environment, missing scope declarations can cause overbroad execution rights, make review harder, and increase the chance that file or network actions occur outside user expectations.
The instructions tell users to configure an API key and say it will be saved to config.json, but they do not warn that the key is a sensitive secret, where the file resides, what permissions protect it, or how to rotate or remove it. This can lead to accidental exposure through weak filesystem permissions, source control commits, backups, or sharing of the config file.
The package description is written entirely in Chinese, which imposes a specific language on users without any opt-in or indication that the skill is intentionally region-specific. Under the policy, language constraints should either be optional for the user or clearly justified as locale-specific.
The script's natural-language interface, including help text, status messages, and error messages, is entirely in Chinese. This forces a specific language on all users without any opt-in, fallback, or documented regional justification, which matches the language/locale policy violation category.
The skill transmits user-supplied queries and the API key to an external third-party service, which creates a real data-exposure boundary. In an agent context, queries may contain sensitive prompts, internal data, or secrets, and the script performs no consent check, redaction, allowlisting, or warning before sending them off-host.
}
$config = Get-Content $configPath | ConvertFrom-Json
$apiKey = $config.api_key
$apiUrl = "https://api.tavily.com/search"
# 搜索功能
if ($Command -eq "search" -or $Command -eq "research" -or $Command -eq "image") {
User-facing messages, help text, and command descriptions are hardcoded in Chinese throughout the script. This creates a language/locale policy issue because the tool does not offer user opt-in or a configurable language selection, and no justified region-specific constraint is documented.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
CONFIG_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config.json")
API_URL = "https://api.tavily.com/search"
def save_config(api_key):
with open(CONFIG_PATH, "w", encoding="utf-8") as f:
The skill stores the API key in a local plaintext config.json file without any warning, permission hardening, or safer secret-handling mechanism. On multi-user systems or insecure working directories, this can expose credentials to other local users, backups, or accidental source-control inclusion, leading to unauthorized API use.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
}
try:
response = requests.post(API_URL, json=body, timeout=30)
response.raise_for_status()
result = response.json()
The manifest description includes '引用生成' and says the skill returns '结构化搜索结果'. The script simply forwards Tavily API results and, unless --format json is selected, renders them as console text; there is no code that generates citations or consistently returns a structured result format itself.
The manifest says the skill supports '深度研究' and the help text exposes a dedicated 'research' command, which implies expanded research behavior. In code, the 'research' command only changes search_depth to advanced for the same /search API call, and the declared MaxPages parameter shown in help is never used, so the implemented behavior is much narrower than advertised.
The manifest describes a Tavily search capability focused on web search, deep research, image search, and structured results. This code additionally implements local credential storage by writing the API key to config.json, which is a persistence capability outside the core search functionality as described.
No suspicious patterns detected.