T08 · Insecure Dependencies
- Location
SKILL.md:9- Finding
Unpinned Third-Party Dependency Creates a Supply-Chain Risk
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This is a disclosed multi-provider search skill with normal outbound search behavior, but users should understand its subprocess, scraping, caching, and dependency risks before installing.
Install only if you are comfortable sending search terms to the listed external providers, including DuckDuckGo through Scrapling. Use a trusted virtual environment for SCRAPLING_PYTHON_PATH, consider pinning Scrapling dependencies yourself, avoid putting secrets in custom user-agent values or queries, and configure Redis only if caching search results for 24 hours is acceptable.
SKILL.md:9Unpinned Third-Party Dependency Creates a Supply-Chain Risk
search-cluster.py:25Untrusted Search Content Is Inadequately Protected Against Downstream Prompt Injection
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
ctx = ssl.create_default_context()
try:
req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
with urllib.request.urlopen(req, timeout=10, context=ctx) as response:
data = json.loads(response.read().decode())
results = []
if isinstance(data, list) and len(data) > 3:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
ctx = ssl.create_default_context()
try:
req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
with urllib.request.urlopen(req, timeout=10, context=ctx) as response:
data = json.loads(response.read().decode())
results = []
if isinstance(data, list) and len(data) > 3:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
ctx = ssl.create_default_context()
try:
req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
with urllib.request.urlopen(req, timeout=10, context=ctx) as response:
data = json.loads(response.read().decode())
results = []
if isinstance(data, list) and len(data) > 3:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
ctx = ssl.create_default_context()
try:
req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
with urllib.request.urlopen(req, timeout=10, context=ctx) as response:
data = json.loads(response.read().decode())
results = []
if isinstance(data, list) and len(data) > 3:
The declared behavior says the skill aggregates results from specific providers, but the implementation reportedly uses DuckDuckGo scraping and a stealth-fetching mechanism that is not transparently disclosed. This mismatch is dangerous because it can conceal unexpected network destinations and collection methods, undermining user consent, security review, and policy enforcement; the use of 'stealth' scraping makes the discrepancy more suspicious in this context.
The skill advertises executable components and requires environment variables, network access, and shell/subprocess behavior, but it does not declare an explicit tool scope such as permissions or allowed-tools. This creates a governance gap where reviewers and runtime policy engines cannot easily constrain what the skill is allowed to access, increasing the risk of unintended command execution, secret exposure, or outbound requests.
User search terms are sent to third-party services, which can expose potentially sensitive queries to external providers without any explicit disclosure or consent mechanism in the code path. In a search aggregation skill, this is contextually significant because the core function fans the same query out to multiple services, increasing privacy exposure.
Executing an external helper process is a meaningful security-relevant design choice because it introduces another executable component and trust boundary. In this skill's context, that is more dangerous than a typical search aggregator because compromise of the helper script or interpreter path can alter behavior or execute arbitrary code.
The skill launches a secondary script for one provider without any user-facing indication, obscuring that additional local code will run as part of a search request. Hidden execution paths reduce transparency and can surprise operators, especially in agent environments where capabilities should be explicit.
The skill spawns an external Python interpreter and helper script to fulfill search functionality, which expands the attack surface beyond normal in-process HTTP requests. While subprocess.run is invoked without shell=True, the executable path comes from an environment variable, so a compromised or untrusted environment can redirect execution to an unintended binary.
if not os.path.exists(SCRAPLING_PYTHON) or not os.path.exists(STEALTH_SCRIPT):
return []
try:
result = subprocess.run([SCRAPLING_PYTHON, STEALTH_SCRIPT, query], capture_output=True, text=True, timeout=30)
if result.returncode == 0:
data = json.loads(result.stdout)
for item in data:
SCRAPLING_PYTHON is taken from an environment variable and executed directly, creating a trust boundary violation that can lead to arbitrary code execution if the environment is attacker-controlled or misconfigured. The existence check only confirms that a path exists, not that it is the intended interpreter or a safe executable.
if not os.path.exists(SCRAPLING_PYTHON) or not os.path.exists(STEALTH_SCRIPT):
return []
try:
result = subprocess.run([SCRAPLING_PYTHON, STEALTH_SCRIPT, query], capture_output=True, text=True, timeout=30)
if result.returncode == 0:
data = json.loads(result.stdout)
for item in data:
The Google News RSS query forces hl=en-US, gl=US, and ceid=US:en, which imposes a specific language and region regardless of user preference. This is a natural-language policy issue because the skill does not offer a locale choice or document a justified region-specific constraint.
The implementation sends user queries to DuckDuckGo and scrapes its HTML results even though the skill metadata says it aggregates Google CSE, GNews RSS, Wikipedia, Reddit, and Scrapling. This is a real integrity and transparency issue because users and downstream systems may rely on the declared data sources for privacy, compliance, or trust decisions, but the code silently uses an undeclared third-party endpoint instead.
This markdown file documents multiple outbound network endpoints and notes required credentials such as GOOGLE_API_KEY, but it does not include any warning that using these APIs transmits queries or metadata to third parties. Under the markdown variant of SQP-2, descriptions should warn about behaviors that may affect user privacy or external data handling.
The file specifies https://en.wikipedia.org/w/api.php, which fixes the skill behavior to the English Wikipedia domain. Because no user choice, opt-in, or documented justification is provided, this appears to impose a specific language/locale in natural-language configuration.
The code transmits the user's search query to an external website without any explicit disclosure, consent flow, or warning. In a search skill this may seem routine, but it still creates a privacy risk because user-supplied queries can contain sensitive terms, and the hidden transmission is more concerning here because the destination is also undeclared in the manifest.
No suspicious patterns detected.