T08 · Insecure Dependencies
- Location
SKILL.md:10- Finding
Unpinned Third-Party Dependencies Create a Supply-Chain Risk
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This CS2 stats skill mostly does what it says, but its demo-analysis code lets API-supplied data control downloads and cache paths, creating local file and network/resource risks.
Install only if you are comfortable running it in a constrained environment with a Leetify API key and local Steam ID mappings. Prefer a revised version that pins dependencies, sanitizes cache filenames, confines cache reads and writes, allowlists replay hosts, and enforces download, decompression, parsing, and HTTP timeouts.
SKILL.md:10Unpinned Third-Party Dependencies Create a Supply-Chain Risk
scripts/analyze_last_demo.py:1075Remote Match Identifier Can Escape the Cache Directory
scripts/analyze_last_demo.py:116API-Controlled Replay URL Is Downloaded Without Destination or Resource Restrictions
scripts/profile_with_stats.py:15Authenticated API Requests Have No Timeout
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
headers = {"Authorization": f"Bearer {API_KEY}"}
url = f"{API_BASE}{endpoint}"
try:
response = requests.get(url, params=params, headers=headers)
if response.status_code == 200:
return {"success": True, "data": response.json()}
else:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
headers = {"Authorization": f"Bearer {API_KEY}"}
url = f"{API_BASE}{endpoint}"
try:
response = requests.get(url, params=params, headers=headers)
if response.status_code == 200:
return {"success": True, "data": response.json()}
else:
This is a clear description-behavior mismatch. The declared purpose centers on querying Leetify for CS2 analytics features, but the supplied code is a local storage utility for mapping usernames to Steam IDs. Its primary purpose is identity/mapping persistence using a JSON file, not analytics or API-backed stat retrieval. While Steam ID mapping could be a supporting component of a broader CS2 stats skill, this code chunk itself does not implement any of the declared user-facing capabilities and instead introduces a separate undeclared capability: storing and managing user identifiers locally.
The skill advertises executable capabilities including environment access, shell execution, network access, and file read/write, but does not declare any explicit tool scope such as permissions or allowed-tools. This weakens containment and reviewability because an agent may invoke broader capabilities than a user or platform expects, increasing the chance of unintended command execution, data access, or exfiltration if the skill or its companion scripts are modified or abused.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
# ==========================================
def get_steam_id(username: str) -> str:
result = subprocess.run(
[sys.executable, STEAM_IDS_SCRIPT, "get", "--username", username],
capture_output=True, text=True
)
Multiple user-visible status and error messages are written only in Russian, such as the message on this line. This imposes a specific language choice on all users without offering a locale selection or documenting a justified region-specific constraint.
This API error message is presented only in Russian. Under the language/locale policy, user-facing text should not force a specific language unless the user opted in or the regional limitation is explicitly documented.
The download and extraction progress messages in this block are user-facing and exclusively Russian. Because the script provides no language selection and no documented locale restriction, these messages violate the language/locale policy.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
if local_path.endswith('.bz2'):
print("📦 Распаковка .bz2...", file=sys.stderr)
subprocess.run(['bunzip2', '-f', local_path], check=True)
local_path = local_path[:-4]
elif local_path.endswith('.gz'):
print("📦 Распаковка .gz...", file=sys.stderr)
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
local_path = local_path[:-4]
elif local_path.endswith('.gz'):
print("📦 Распаковка .gz...", file=sys.stderr)
subprocess.run(['gunzip', '-f', local_path], check=True)
local_path = local_path[:-3]
if not os.path.isfile(local_path):
This missing-match error is shown only in Russian, imposing a fixed locale on all users. The file does not provide an alternative language or indicate that the tool is restricted to Russian-speaking users.
These cache/download/parse/save status messages are visible to the user and written only in Russian. Since the script accepts general CLI input and does not document a Russian-only audience, this is a language policy violation.
The final completion message is user-facing and exclusively Russian. This forces a language choice on every user without offering opt-in, fallback, or documented justification.
The script emits multiple user-facing strings in Russian, such as headings and labels for profile statistics, without offering any language selection or indicating that the skill is intentionally restricted to Russian-speaking users. This is a natural-language locale constraint that can violate language/locale policy when imposed without opt-in.
The code makes authenticated HTTP requests to an external Leetify API using a Steam identifier and then processes match data, but there is no confirmation prompt or user-facing notice that account-related data will be sent to a third-party service. For code files, outbound network transmission of user or system data should have some visible disclosure unless the warning is documented elsewhere or the behavior is clearly disclosed as part of the skill description, which is not shown in this file.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
script_dir = os.path.dirname(os.path.abspath(__file__))
try:
# Resolve username to Steam ID
res = subprocess.check_output(
['python3', os.path.join(script_dir, 'steam_ids.py'), 'get', '--username', args.username],
text=True
).strip()
The manifest describes a skill for retrieving CS2 statistics, match analysis, and gameplay insights from the Leetify API. This file adds process-spawning behavior by invoking another script via subprocess, which is a broader execution capability than the stated API-query purpose requires.
The script sends user-supplied Steam IDs and retrieves profile/match data from an external third-party service without any user-facing disclosure or consent mechanism. In an agent skill context, this can cause unexpected external data transfer and privacy issues because users may not realize their identifiers and query context are being shared with Leetify.
The manifest describes a skill for retrieving CS2 player statistics, match analysis, gameplay insights, comparison, and season stats from the Leetify API. This file instead implements persistent local storage and management of Telegram/username-to-Steam-ID mappings, including save, list, and delete operations, which is a separate identity-management capability not described in the manifest.
The code persists Telegram-linked identity data, including username, optional display name, and optional telegram_id, to a local JSON file without any apparent minimization, retention controls, or access protections. For a Leetify stats skill, storing cross-platform identity linkage is more sensitive than needed and creates unnecessary privacy risk if the file is exposed, reused, or retained indefinitely.
This code emits the resolved Steam ID directly to stderr, which exposes a user-linked identifier during execution. Although it is logged visibly, the script docstring and argument help do not disclose that a personal identifier will be displayed and used as part of the analysis flow.
The manifest describes a skill for CS2 player statistics, match analysis, and gameplay insights from the Leetify API, but this script's documented and implemented entry point is comparing players by Telegram usernames and resolving them through a local steam_ids mapping first. That introduces an identity-lookup behavior not reflected in the manifest's stated scope.
The error messages at L23 and L28 are hard-coded in Russian ("Steam ID не найден...") with no indication that the user can choose another language. This can violate a language/locale policy when skills are expected not to force a specific language without user opt-in.
The error message is hard-coded in Russian ("Steam ID не найден") with no indication that the skill is region-specific or that users can choose their language. This creates a natural-language locale policy issue because the script imposes a specific language on all users.
The script reads LEETIFY_API_KEY from the environment and uses it for authenticated API access, but there is no comment, docstring, or user-facing notice explaining that a credential is required and will be used. Under the code-file criteria, access to sensitive environment variables should have some form of disclosure unless clearly documented elsewhere, which is not evident in this file.
No suspicious patterns detected.