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## 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
## 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)

Lp3

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.

Missing User Warnings

Medium
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.

External Transmission

Medium
Category
Data Exfiltration
Content
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
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
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.

Description-Behavior Mismatch

Low
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.

Static analysis

No suspicious patterns detected.