Back to skill

Security audit

gemini-smart-search

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its Gemini search purpose, but its documented wrapper can execute a local .env.local file as shell code and the Python entrypoint imports arbitrary variables from that file.

Install only if you are comfortable sending search queries to Google's Gemini API with Google Search grounding. Prefer the Python entrypoint, keep .env.local under your control with only SMART_SEARCH_GEMINI_API_KEY or GEMINI_API_KEY, and avoid the shell wrapper unless the .env.local file is trusted and simple key-value data.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/gemini_smart_search.sh:7
Finding
Executable and Overly Broad Loading of Repository-Local Environment Configuration<![CDATA[ ## Vulnerability Details **File Location**: `scripts/gemini_smart_search.sh:7-12`; related behavior in `scripts/gemini_smart_search.py:66-84` **Vulnerability Type**: Unsafe configuration-file execution and unrestricted environment-variable import **Risk Level**: Medium ### Complete Vulnerable Code `scripts/gemini_smart_search.sh:7-12`: ```bash if [ -f "$SKILL_DIR/.env.local" ]; then set -a # shellcheck disable=SC1090 . "$SKILL_DIR/.env.local" set +a fi ``` Related Python configuration loading in `scripts/gemini_smart_search.py:66-84`: ```python def load_repo_local_env() -> None: if os.environ.get("GEMINI_SMART_SEARCH_SKIP_LOCAL_ENV") == "1": return script_dir = Path(__file__).resolve().parent env_path = script_dir.parent / ".env.local" if not env_path.exists(): return for raw_line in env_path.read_text(encoding="utf-8").splitlines(): line = raw_line.strip() if not line or line.startswith("#") or "=" not in line: continue key, value = line.split("=", 1) key = key.strip() value = value.strip() if not key or key in os.environ: continue if value and len(value) >= 2 and value[0] == value[-1] and value[0] in {'"', "'"}: value = value[1:-1] os.environ[key] = value ``` ### Technical Analysis The shell wrapper loads `.env.local` using the Bash `.` command. This does not parse the file as passive key-value configuration; it executes the entire file as shell code in the wrapper's process. A crafted file can contain command substitutions, shell commands, redirections, functions, or other Bash constructs. These execute before the wrapper invokes the Python search implementation. The Python loader avoids direct shell execution, but it accepts every key found in `.env.local` and inserts it into `os.environ`. The Skill only needs `SMART_SEARCH_GEMINI_API_KEY` and the compatibility fallback `GEMINI_API_KEY`. Importing ...[truncated 2369 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Remove shell sourcing entirely.** The wrapper should invoke the canonical Python implementation without evaluating `.env.local`: ```bash exec python3 "$SCRIPT_DIR/gemini_smart_search.py" "$@" ``` 2. **Use one non-executable configuration parser.** Keep `.env.local` handling exclusively in Python and treat it strictly as data. 3. **Allowlist required variables.** Only accept: - `SMART_SEARCH_GEMINI_API_KEY` - `GEMINI_API_KEY` Ignore or reject every other key. 4. **Avoid process-wide environment mutation.** Parse the required values into local variables and pass the resolved API key directly to the request function rather than inserting arbitrary entries into `os.environ`. 5. **Validate configuration syntax.** Reject malformed names, duplicate assignments, unexpected quoting, multiline values, and shell constructs. A strict variable-name expression such as `^[A-Z_][A-Z0-9_]*$` should be used in addition to the allowlist. 6. **Check secret-file security.** Where supported, reject `.env.local` when it is a symbolic link, is not owned by the expected user, or is writable by group or other users. Recommend permissions equivalent to `0600`. 7. **Update documentation.** State that `.env.local` is parsed as non-executable configuration and must contain only the supported API-key fields. 8. **Add regression tests.** Verify that shell syntax in `.env.local` is never executed and that unrelated variables such as proxy settings are not imported. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (14)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill description claims Gemini-backed web search with model routing, grounding, fallback, and JSON output, but the supplied skill file also references artifact preparation and release packaging workflows that are unrelated to the declared purpose, while the static finding indicates the actual code may not perform the described search behavior at all. This mismatch is dangerous because users and agents may trust the skill for one capability while it executes different file-system or packaging actions, enabling deceptive or unauthorized behavior under a benign-looking label.

Credential Access

High
Category
Privilege Escalation
Content
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
SKILL_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"

if [ -f "$SKILL_DIR/.env.local" ]; then
  set -a
  # shellcheck disable=SC1090
  . "$SKILL_DIR/.env.local"
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
SKILL_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"

if [ -f "$SKILL_DIR/.env.local" ]; then
  set -a
  # shellcheck disable=SC1090
  . "$SKILL_DIR/.env.local"
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
SKILL_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"

if [ -f "$SKILL_DIR/.env.local" ]; then
  set -a
  # shellcheck disable=SC1090
  . "$SKILL_DIR/.env.local"
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
SKILL_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"

if [ -f "$SKILL_DIR/.env.local" ]; then
  set -a
  # shellcheck disable=SC1090
  . "$SKILL_DIR/.env.local"
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
pass "non-destructive JSON smoke passes for mode=$mode"
done

if git -C "$REPO_DIR" check-ignore .env.local >/dev/null 2>&1; then
  pass ".env.local is gitignored"
else
  fail ".env.local is not ignored"
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
pass "non-destructive JSON smoke passes for mode=$mode"
done

if git -C "$REPO_DIR" check-ignore .env.local >/dev/null 2>&1; then
  pass ".env.local is gitignored"
else
  fail ".env.local is not ignored"
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
pass "non-destructive JSON smoke passes for mode=$mode"
done

if git -C "$REPO_DIR" check-ignore .env.local >/dev/null 2>&1; then
  pass ".env.local is gitignored"
else
  fail ".env.local is not ignored"
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill declares execution requirements and instructs agents to run local scripts that use environment variables, local files, and outbound network access, but it does not declare an explicit tool scope such as allowed-tools or permissions. That gap can cause agents or hosting systems to grant broader capabilities than reviewers expect, reducing least-privilege and making misuse or accidental overreach harder to control.

Skill Enumeration

Medium
Category
Agent Snooping
Content
### Case 8 — Wrong entrypoint assumption: try to execute `SKILL.md` as Python
**Command**
```bash
python3 skills/gemini-smart-search/SKILL.md
```
**Observed**
- syntax error, obviously
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Intent-Code Divergence

Medium
Confidence
89% confidence
Finding
Lines L051-L059 state that transient network errors and single-model quota failures should not trigger escalation. But L061-L063 explicitly says the current v1 code sets `escalation.should_open_issue=true` when the full fallback chain is exhausted, even if the underlying causes were retryable upstream failures, which contradicts the earlier stated policy.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This code sends the user-provided query to an external Google Gemini endpoint and explicitly enables Google Search grounding, which may transmit user-entered content off-system. Although the module docstring mentions Gemini and grounding, there is no runtime confirmation, warning, or user-facing disclosure near execution to alert users that their query will be sent to external services.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The script performs `rm -rf "$STAGE_DIR"`, which irreversibly deletes the staging directory contents. Although the script later logs success, there is no warning, confirmation prompt, or prior user-facing notice around this destructive operation in the script itself.

Missing User Warnings

Low
Confidence
91% confidence
Finding
The document explicitly describes sourcing a repo-local `.env.local` file and performing live API requests against Google endpoints using that local credential context. Even though no secret values are printed, this guidance normalizes credential use and outbound probing without an explicit warning, which can lead operators or future automation to run networked actions with local secrets in ways they may not expect.

Static analysis

No suspicious patterns detected.