Back to skill

Security audit

Amazon Reviews Api Skill

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it claims, but it asks users to provide an API key through the agent conversation and collects reviewer profile data without clear privacy limits.

Review this carefully before installing. Use it only when you explicitly want live Amazon review extraction through BrowserAct, configure BROWSERACT_API_KEY directly in your environment or secret manager, and do not paste API keys into chat. Be aware that extracted data can include reviewer names and profile links, so use and store it only where you have a legitimate basis to do so.

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 (2)

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:34
Finding

API Credential Solicitation Through Agent Conversation

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:34-37; scripts/amazon_reviews_api.py:86-94
Vulnerability Type: API credential exposure through an insecure secret-provisioning workflow
Risk Level: Medium

Vulnerable Code and Instructions

SKILL.md:34-37:

markdown
## 🔑 API Key Setup
Before running, check the `BROWSERACT_API_KEY` environment variable. If not set, do not take other measures; ask and wait for the user to provide it.
**Agent must inform the user**:
> "Since you haven't configured the BrowserAct API Key, please visit the [BrowserAct Console](https://www.browseract.com/reception/integrations) to get your Key."

scripts/amazon_reviews_api.py:86-94:

python
if not api_key:
    print("\n[!] ERROR: BrowserAct API Key is missing.", flush=True)
    print("Please follow these steps:", flush=True)
    print("1. Go to: https://www.browseract.com/reception/integrations", flush=True)
    print("2. Copy your API Key.", flush=True)
    print("3. Provide it to me or set it as an environment variable (BROWSERACT_API_KEY).", flush=True)
    sys.exit(1)

Technical Analysis

The Skill explicitly directs the Agent to ask the user for a reusable BrowserAct API key and tells the user to “Provide it to me.” This creates an insecure credential-provisioning path through the conversation rather than requiring direct configuration through a local environment variable or secret-management facility.

Credentials submitted through an Agent conversation may be retained in conversation history, execution traces, observability systems, support records, or other platform logs. The script does not itself print the value or transmit it to unrelated destinations; its legitimate network use sends the key to https://api.browseract.com as a Bearer token. The vulnerability is therefore the unnecessary solicitation and handling of the secret by the Agent, not the required authenticated BrowserAct API call.

Attack Path

  1. The user invokes th ...[truncated 1054 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove all instructions asking users to provide API keys through the Agent conversation.
  2. Replace “Provide it to me” with instructions to configure BROWSERACT_API_KEY directly in the local process environment or an approved secret-management interface.
  3. Make the missing-key message state explicitly that the credential must not be pasted into chat, logs, command-line arguments, or source files.
  4. Keep the key out of standard output, error messages, exception details, and task results.
  5. Document key rotation and revocation procedures for users who may already have disclosed a key.
  6. Where supported, use narrowly scoped, short-lived credentials with usage limits rather than long-lived account-wide API keys.
  7. Ensure Agent and platform telemetry applies secret redaction to values matching the BrowserAct credential format as defense in depth.

A safer failure message would be:

python
if not api_key:
    print(
        "BROWSERACT_API_KEY is missing. Configure it directly in your "
        "environment or approved secret store. Do not paste the key into chat.",
        flush=True,
    )
    sys.exit(1)

T09 · Insecure Skill Coding Practices

Note
Location
scripts/amazon_reviews_api.py:27
Finding

Unbounded HTTP Requests and Task Polling Permit Indefinite Execution

Content
View full analysis

Vulnerability Details

File Location: scripts/amazon_reviews_api.py:27-72
Vulnerability Type: Missing HTTP timeouts and unbounded polling
Risk Level: Low

Vulnerable Code

python
# 1. Start Task
print(f"Start Task", flush=True)
try:
    res = requests.post(f"{API_BASE_URL}/run-task-by-template", json=payload, headers=headers).json()
except Exception as e:
    print(f"Error: Connection to API failed - {e}", flush=True)
    return None

if "id" not in res:
    # Check for authorization error
    if "Invalid authorization" in str(res):
        print(f"Error: Invalid authorization. Please check your BrowserAct API Key.", flush=True)
    else:
        print(f"Error: Could not start task. Response: {res}", flush=True)
    return None

task_id = res["id"]
print(f"Task started. ID: {task_id}", flush=True)

# 2. Poll for Completion
while True:
    try:
        status_res = requests.get(f"{API_BASE_URL}/get-task-status?task_id={task_id}", headers=headers).json()
        status = status_res.get("status")

        timestamp = datetime.datetime.now().strftime("%H:%M:%S")
        print(f"[{timestamp}] Task Status: {status}", flush=True)

        if status == "finished":
            print(f"[{timestamp}] Task finished successfully.", flush=True)
            break
        elif status in ["failed", "canceled"]:
            print(f"Error: Task {status}. Please check your BrowserAct dashboard.", flush=True)
            return None
    except Exception as e:
        timestamp = datetime.datetime.now().strftime("%H:%M:%S")
        print(f"[{timestamp}] Polling error: {e}. Retrying...", flush=True)

    time.sleep(10)

# 3. Get Results
try:
    task_info = requests.get(f"{API_BASE_URL}/get-task?task_id={task_id}", headers=headers).json()

    output = task_info.get("output", {})
    result_string = output.get("string")

Technical Analysis

All three HTTP operations omit explicit connection and read timeouts. A remote endpoint or network intermediary th ...[truncated 1834 chars]

Remediation
View remediation

Remediation Suggestions

  1. Set explicit connection and read timeouts on every HTTP request, for example timeout=(5, 30).
  2. Add a fixed overall task deadline or maximum poll count.
  3. Stop polling and return a clear error when the deadline is exceeded.
  4. Treat missing or unknown status values as bounded errors rather than polling indefinitely.
  5. Use response.raise_for_status() before parsing JSON.
  6. Handle requests.Timeout, requests.ConnectionError, HTTP errors, and JSON-decoding failures separately.
  7. Apply bounded exponential backoff with a maximum delay for transient failures.
  8. Consider using a requests.Session for controlled connection reuse while retaining per-request timeouts.
  9. Ensure the external retry described by the Skill remains limited to one execution and does not multiply internal unbounded retries.

Example hardening pattern:

python
MAX_POLLS = 60
REQUEST_TIMEOUT = (5, 30)

response = requests.post(
    f"{API_BASE_URL}/run-task-by-template",
    json=payload,
    headers=headers,
    timeout=REQUEST_TIMEOUT,
)
response.raise_for_status()
res = response.json()

for attempt in range(MAX_POLLS):
    response = requests.get(
        f"{API_BASE_URL}/get-task-status",
        params={"task_id": task_id},
        headers=headers,
        timeout=REQUEST_TIMEOUT,
    )
    response.raise_for_status()
    status_res = response.json()
    status = status_res.get("status")

    if status == "finished":
        break
    if status in {"failed", "canceled"}:
        return None
    if status not in {"pending", "running"}:
        print(f"Error: Unknown task status: {status}", flush=True)
        return None

    time.sleep(10)
else:
    print("Error: Task polling deadline exceeded.", flush=True)
    return None
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 (5)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill requires environment access and network-capable execution but does not declare an explicit tool scope or permission boundary. That creates ambiguity about what the agent may execute or access, increasing the risk of over-broad invocation, unintended secret exposure, or network actions outside user expectations.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill is written to be 'proactively' applied for a wide range of generic review-analysis requests, which can cause the agent to invoke external scraping/API behavior when a user only asked for analysis or summarization. In context, this is more dangerous because the skill performs networked data extraction and may collect third-party data without an explicit, narrowly scoped user request.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill explicitly advertises collection of reviewer profiles and profile links but provides no privacy notice, minimization guidance, or restriction on handling potentially personal data. In this context, harvesting identifiable reviewer information for market research increases privacy, compliance, and misuse risk beyond ordinary product-review analysis.

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/amazon_reviews_api.py (reported line 15)May include surrounding context.

python
# API Configuration
TEMPLATE_ID = "77817507798321724"
API_BASE_URL = "https://api.browseract.com/v2/workflow"

def run_amazon_reviews_task(api_key, asin):
    headers = {"Authorization": f"Bearer {api_key}"}

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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/amazon_reviews_api.py (reported line 29)May include surrounding context.

python
# 1. Start Task
    print(f"Start Task", flush=True)
    try:
        res = requests.post(f"{API_BASE_URL}/run-task-by-template", json=payload, headers=headers).json()
    except Exception as e:
        print(f"Error: Connection to API failed - {e}", flush=True)
        return None

Static analysis

No suspicious patterns detected.