Back to skill

Security audit

Kiro Search Aggregator

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent search aggregator, but it needs review because a SerpAPI error can expose the API key in saved output or logs.

Review before installing. Use only non-sensitive search queries, and avoid enabling the Scholar source with a real SerpAPI key until the error handling redacts api_key values from exceptions, stdout, latest.json, and latest.md. The provider integrations themselves are expected for the skill, but the credential exposure bug should be fixed.

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/search_aggregator.py:167
Finding

SerpAPI Credential Disclosure Through Error Reporting

Content
View full analysis

Vulnerability Details

File Location: scripts/search_aggregator.py:47-48, scripts/search_aggregator.py:71-73, scripts/search_aggregator.py:167-176, and scripts/search_aggregator.py:325-344
Vulnerability Type: API credential exposure through unsanitized URL logging
Risk Level: Medium

Vulnerable Code

python
if params:
    query = urllib.parse.urlencode({k: v for k, v in params.items() if v is not None}, doseq=True)
    url = f"{url}?{query}"
python
msg = f"HTTP {exc.code} {exc.reason} @ {url}"
if detail:
    msg += f" | response={detail[:700]}"
raise RuntimeError(msg) from exc
python
payload = request_json(
    method="GET",
    url=SERPAPI_BASE,
    params={
        "engine": "google_scholar",
        "q": query,
        "num": per_source,
        "api_key": key,
    },
)
python
for source in selected:
    fn = source_map[source]
    try:
        merged.extend(fn(args.query, args.per_source))
    except Exception as exc:
        errors[source] = str(exc)

result = {
    "generated_at": now_iso(),
    "query": args.query,
    "sources": selected,
    "source_status": source_status(selected),
    "errors": errors,
    "summary": summarize(merged),
    "results": [asdict(i) for i in merged],
}

out_dir = Path(args.out_dir)
out_dir.mkdir(parents=True, exist_ok=True)
(out_dir / "latest.json").write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")
(out_dir / "latest.md").write_text(render_markdown(result), encoding="utf-8")

print(json.dumps(result, ensure_ascii=False, indent=2))

Technical Analysis

The Scholar search implementation passes SERPAPI_API_KEY as the api_key query parameter. request_json() appends all parameters to the request URL. If SerpAPI returns an HTTP error, the exception message incorporates that complete URL without redacting sensitive parameters.

The c ...[truncated 2008 chars]

Remediation
View remediation

Remediation Suggestions

  1. Never include an unsanitized request URL in an exception when it may contain credentials. Redact sensitive query parameters before constructing diagnostic messages:

    python
    def sanitized_url(url: str) -> str:
        parts = urllib.parse.urlsplit(url)
        params = urllib.parse.parse_qsl(parts.query, keep_blank_values=True)
        safe_params = [
            (key, "[REDACTED]" if key.lower() in {"api_key", "key", "token", "access_token"} else value)
            for key, value in params
        ]
        return urllib.parse.urlunsplit(
            (parts.scheme, parts.netloc, parts.path, urllib.parse.urlencode(safe_params), parts.fragment)
        )
    
  2. Use the sanitized value in errors:

    python
    msg = f"HTTP {exc.code} {exc.reason} @ {sanitized_url(url)}"
    
  3. If SerpAPI supports an authentication header for this endpoint, prefer it over a query-string credential. URL credentials are more likely to appear in logs, proxies, telemetry, and exception messages.

  4. Store only structured, non-sensitive diagnostics in result["errors"], such as the provider name, HTTP status code, and a generic failure description.

  5. Treat provider response bodies as potentially sensitive and untrusted. Sanitize them before writing them to reports or logs, and avoid including them unless explicitly requested for debugging.

  6. Add regression tests that configure recognizable dummy secrets, exercise HTTP error paths, and assert that no secret appears in exception messages, stdout, latest.json, or latest.md.

  7. Rotate any real SerpAPI key that may already have appeared in generated output or logs, and remove or restrict access to affected artifacts where feasible.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (5)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill declares required environment variables and describes behavior that performs network access and writes output files, but it does not declare any explicit tool scope such as permissions or allowed-tools. That omission weakens least-privilege controls and reduces transparency to users and hosting platforms about what the skill can access and do.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill sends user-supplied queries to multiple third-party providers and writes results to local files, but the description does not warn users that their prompts may leave the local environment and be persisted on disk. This creates a privacy and data-handling risk, especially if users include sensitive, proprietary, or regulated information in search queries.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/search_aggregator.py (reported line 27)May include surrounding context.

python
SERPER_BASE = "https://google.serper.dev"
SERPAPI_BASE = "https://serpapi.com/search.json"
X_RECENT_SEARCH = "https://api.x.com/2/tweets/search/recent"


@dataclass

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script stores the raw query and aggregated results to predictable local files without any consent, warning, or redaction. Search queries can contain sensitive user intent, research topics, or proprietary information, and the saved results may expose that data to other local users, processes, backups, or later exfiltration.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The manifest describes aggregating and ranking search results and outputting a concise brief. In addition to generating output, the code creates an output directory and writes both a full JSON dataset and markdown file to disk, which is extra behavior not stated in the manifest description.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.