Back to skill

Security audit

Google SERP Search API

Security checks for vulnerabilities and agentic risk

Overview

The skill is a focused Google search API wrapper, but it passes the Just Serp API key through command-line arguments where it may be locally exposed.

Review before installing. The search behavior is coherent, but use a limited-scope Just Serp API key if possible, avoid sensitive searches or precise personal location parameters, and prefer a version that reads the API key directly from the environment instead of passing it with --api-key.

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
bin/run.mjs:239
Finding

API Key Exposed Through Process Command-Line Arguments

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:60-68, bin/run.mjs:239-255, and bin/run.mjs:308-324
Vulnerability Type: Command-line credential exposure
Risk Level: Medium

The documented invocation passes the API key through the --api-key command-line argument:

bash
node {baseDir}/bin/run.mjs --operation "search" --api-key "$JUST_SERP_API_KEY" --params-json '{"query":"<query>"}'

The implementation requires the argument and inserts its value into the authentication header:

javascript
if (!args.apiKey) {
  fail("Missing required --api-key argument.");
}

const params = parseParams(args.paramsJson);
applyDefaults(operation, params);
validateRequired(operation, params);

const baseUrl = manifest.baseUrl;
const url = new URL(operation.path, ensureBaseUrl(baseUrl));
applyPathParams(operation, params, url);
applyQueryParams(operation, params, url);

const requestInit = {
  headers: {
    "accept": "application/json",
    "X-API-Key": args.apiKey,
  },
  method: operation.method,
};

The command-line parser stores the secret directly from the process argument vector:

javascript
function parseArgs(argv) {
  const parsed = { apiKey: null, operation: null, paramsJson: "{}" };
  for (let index = 0; index < argv.length; index += 1) {
    const flag = argv[index];
    const value = argv[index + 1];
    if (flag === "--operation") {
      parsed.operation = value;
      index += 1;
      continue;
    }
    if (flag === "--params-json") {
      parsed.paramsJson = value;
      index += 1;
      continue;
    }
    if (flag === "--api-key") {
      parsed.apiKey = value;
      index += 1;
      continue;
    }

Technical Analysis

Although the documented command obtains the credential from the JUST_SERP_API_KEY environment variable, the shell expands that variable before starting Node.js. The resulting plaintext A ...[truncated 2079 chars]

Remediation
View remediation

Remediation Suggestions

  1. Read the credential directly from the process environment rather than requiring it as a command-line argument:

    javascript
    const apiKey = process.env.JUST_SERP_API_KEY;
    if (!apiKey) {
      fail("Missing required JUST_SERP_API_KEY environment variable.");
    }
    
  2. Use apiKey only when constructing the request header:

    javascript
    const requestInit = {
      headers: {
        accept: "application/json",
        "X-API-Key": apiKey,
      },
      method: operation.method,
    };
    
  3. Remove --api-key handling from parseArgs and update SKILL.md to use:

    bash
    JUST_SERP_API_KEY="$JUST_SERP_API_KEY" node {baseDir}/bin/run.mjs \
      --operation "search" \
      --params-json '{"query":"<query>"}'
    

    If the environment is already configured, omit the inline assignment entirely.

  4. If an explicit alternative input mechanism is necessary, accept the secret through protected standard input or a permission-restricted credential file instead of the argument vector.

  5. Ensure error messages, debug output, telemetry, and request logging redact X-API-Key and never serialize the credential.

  6. Rotate any API key that may previously have been exposed through command histories, process-monitoring systems, CI logs, orchestration metadata, or telemetry.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (4)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill invokes a network-capable helper (node .../bin/run.mjs) that sends user-supplied search parameters to an external API, but the manifest does not declare any explicit tool scope such as permissions or allowed-tools. This creates a governance and containment gap: platforms or reviewers may treat the skill as less privileged than it really is, allowing unintended outbound requests and weakening auditability.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This code constructs a request URL from user-provided parameters and sends it to an external API using fetch, including authentication via the X-API-Key header. Aside from error handling, there is no confirmation prompt, log message, comment, or docstring warning that input data will be sent off-system.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The manifest explicitly supports sending user-provided search queries and potentially sensitive localization fields like location, country, UULE, and related identifiers to a third-party API, but it does not include any user-facing disclosure or consent cue. This creates a privacy and data-handling risk because users may provide personal, confidential, or identifying information without realizing it will be transmitted externally.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

This is a manifest-style JSON file, so vague-trigger checks apply. The top-level and operation descriptions broadly describe doing a Google search with a query, but they do not provide any explicit trigger phrases, scope limits, or exclusion conditions, which can make invocation criteria ambiguous in agent environments.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.