Back to skill

Security audit

SMB Sales Boost — B2B Lead Database of SMBs for Cold Outreach & GTM

Security checks for vulnerabilities and agentic risk

Overview

The skill is openly for SMB Sales Boost, but it gives an agent broad billing, account, email-delivery, and export powers through a generic API wrapper with limited containment.

Install only if you trust SMB Sales Boost and are comfortable letting an agent manage more than lead searches: it can export contact data, send lead files by email, delete account objects, buy credits, change plans, and enable automatic future credit purchases. Prefer the environment-variable key path over command-line keys, use explicit credit/export caps, require clear confirmation for any billing, export, email, delete, or auto top-up action, and save exports only to a private directory you control.

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
smb_api.py:92
Finding
Export File Writes Follow Symbolic Links and Can Overwrite Files Outside the Output Directory<![CDATA[ ## Vulnerability Details **File Location**: `smb_api.py`, lines 92-107 **Vulnerability Type**: Symlink-following arbitrary file overwrite **Risk Level**: Medium ### Vulnerable Code ```python for file_entry in data.get("data", {}).get("files", []): # Sanitize filename: strip path components to prevent path traversal raw_name = file_entry.get("fileName", "export.csv") safe_name = os.path.basename(raw_name) # Validate extension against allowlist _, ext = os.path.splitext(safe_name) if ext.lower() not in SAFE_EXTENSIONS: safe_name = safe_name + ".csv" # Write only to designated output directory output_path = os.path.join(output_dir, safe_name) with open(output_path, "wb") as f: f.write(base64.b64decode(file_entry["data"])) ``` ### Technical Analysis The implementation applies `os.path.basename()` and an extension allowlist, which prevent straightforward directory traversal through an API-provided filename. However, the final write uses `open(output_path, "wb")`, which follows existing symbolic links and truncates existing files. Consequently, the claim that files are written only inside the designated output directory is not fully enforced. If an attacker can prepare a symbolic link in that directory and can predict or influence the filename returned by the remote API, the write can be redirected to a file elsewhere on the filesystem. The output directory itself is also caller-controlled through `--output-dir`. The implementation does not: - Reject symbolic-link destinations. - Use no-follow file-opening semantics. - Reject existing destination files. - Verify that the resolved destination remains under an approved directory. - Create export files with an explicit restrictive permission mode. - Enforce limits on base64-encoded or decoded file size. - Enable strict base64 validation. ### Attack Path 1. An attacker gains the ability to create a file or symbolic link in the configured export dir ...[truncated 1456 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Open export files atomically with no-follow and exclusive-create semantics: ```python flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL if hasattr(os, "O_NOFOLLOW"): flags |= os.O_NOFOLLOW fd = os.open(output_path, flags, 0o600) with os.fdopen(fd, "wb") as f: f.write(decoded_data) ``` 2. Reject output paths that already exist rather than silently truncating them. 3. Resolve and validate the output directory against an administrator-approved root. 4. Check that the destination's parent directory resolves inside the approved output root. 5. Reject symbolic-link output directories and destination entries. 6. Generate collision-resistant local filenames rather than relying solely on API-provided names. 7. Decode base64 strictly: ```python decoded_data = base64.b64decode(file_entry["data"], validate=True) ``` 8. Enforce maximum encoded and decoded file sizes before writing to prevent disk exhaustion. 9. Create files with mode `0o600` so exported phone numbers and email addresses are not readable by unrelated local users. 10. Consider writing to a private temporary file and atomically renaming it after all validation succeeds. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
smb_api.py:116
Finding
API Keys Are Accepted and Promoted as Command-Line Arguments<![CDATA[ ## Vulnerability Details **File Location**: `smb_api.py`, lines 116-125 **Vulnerability Type**: Sensitive credential exposure through process arguments **Risk Level**: Low Related command-line examples also appear throughout `SKILL.md`, including lines 403-473, and in `README.md`, including line 52. ### Vulnerable Code ```python parser.add_argument("api_key", nargs="?", default=None, help="API key (smbk_... prefix), or 'none' for unauthenticated endpoints. Falls back to SMB_SALES_BOOST_API_KEY env var.") parser.add_argument("method", help="HTTP method: GET, POST, PATCH, PUT, DELETE") parser.add_argument("endpoint", help="API endpoint path, e.g. /leads, /me, /filter-presets") parser.add_argument("--params", default=None, help="Query parameters as JSON string (for GET requests)") parser.add_argument("--body", default=None, help="Request body as JSON string (for POST/PATCH/PUT)") parser.add_argument("--output-dir", default=DEFAULT_OUTPUT_DIR, help="Directory for saving export files") args = parser.parse_args() # Resolve API key: CLI arg > env var > error api_key = args.api_key or os.environ.get("SMB_SALES_BOOST_API_KEY") ``` The script and documentation promote invocations such as: ```bash python smb_api.py <API_KEY> <METHOD> <ENDPOINT> ``` ### Technical Analysis Command-line arguments are not an appropriate channel for long-lived authentication secrets. Depending on the operating system and execution environment, command arguments may be exposed through: - Process inspection utilities. - Process-monitoring and telemetry systems. - Shell history. - Agent tool-call logs. - Job-runner metadata. - Crash or diagnostic reports. The implementation gives command-line input priority over the environment variable, and numerous examples encourage this usage. This conflicts with documentation asserting that the key is sent only in the Authorization header and is never logged. While the Python program does not explicitly print the key, surrounding process ...[truncated 1523 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove positional API-key support from the command-line interface. 2. Read the key only from `SMB_SALES_BOOST_API_KEY` or an operating-system-backed secret provider. 3. Update all examples in `smb_api.py`, `SKILL.md`, and `README.md` so they never include a key in the command line. 4. Prefer Skill-scoped secret injection rather than globally exported environment variables. 5. If interactive use must be supported, read the key from a non-echoing prompt or standard input rather than an argument: ```python import getpass api_key = os.environ.get("SMB_SALES_BOOST_API_KEY") if not api_key: api_key = getpass.getpass("SMB Sales Boost API key: ") ``` 6. Configure agent runtimes and orchestration systems to redact values matching the `smbk_` key prefix from logs. 7. Clearly document that command history, process arguments, and public chat must never contain the key. 8. Provide a key-revocation and rotation procedure for users who previously followed the positional-argument examples. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (21)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
- `GET /filter-presets` — List all saved presets
- `POST /filter-presets` — Create a preset (requires `name` and `filters` object)
- `DELETE /filter-presets/{id}` — Delete a preset

### 6. Keyword Lists — `/keyword-lists`
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
- `GET /keyword-lists` — List all keyword lists
- `POST /keyword-lists` — Create (requires `name`, optional `keywords` array, `sourceCategories` array max 3)
- `PUT /keyword-lists/{id}` — Update
- `DELETE /keyword-lists/{id}` — Delete

**Keyword list properties:** `name`, `keywords` (wildcard patterns e.g., `*dentist*`), `type` (positive/negative), `pairedListId` (linked positive/negative pair), `sourceCategories` (max 3), `autoRefineEnabled`, `refinementStatus` (running/completed/paused)
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
- `GET /email-schedules` — List schedules
- `POST /email-schedules` — Create (requires `name`, `filterPresetId`, `intervalValue`, `intervalUnit`, `recipients` min 1)
- `PATCH /email-schedules/{id}` — Update (supports `isActive` toggle)
- `DELETE /email-schedules/{id}` — Delete
- `POST /email-schedules/{id}/trigger` — Manually trigger an active schedule to send immediately (rate limited: 1 per 5 minutes)

**Distribution modes:**
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
- `POST /export-formats` — Create (requires `name`, supports `fileType`, `fieldMappings`, split settings, `databaseType`, `combinedAssigneeColumnName`, `combinedFileNameColumnName`)
- `GET /export-formats/{id}` — Get specific format
- `PATCH /export-formats/{id}` — Update
- `DELETE /export-formats/{id}` — Delete
- `POST /export-formats/{id}/set-default` — Set as default

### 9. Export History — `/export-history`
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
- `GET /export-blacklist` — List blacklisted entries
- `POST /export-blacklist` — Add entry (single or batch via `entries` array)
- `DELETE /export-blacklist/{id}` — Remove entry

### 12. Account
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
python smb_api.py smbk_xxx POST /email-schedules/15/trigger

# Delete a filter preset
python smb_api.py smbk_xxx DELETE /filter-presets/42
```

The script outputs JSON to stdout and rate limit headers to stderr. For export requests, files are automatically saved with sanitized filenames.
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The skill metadata frames the capability primarily as lead search and management, but the OpenAPI spec also exposes subscription purchase, plan change, credit purchase, auto top-up, and cancellation flows that can create charges or alter paid account state. In an agent setting, this scope mismatch is dangerous because a user or orchestrator may authorize what appears to be a data-access skill while unintentionally granting billing authority.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
Programmatic purchase and API-key claiming are not necessary to fulfill the core task of querying/managing leads once a key already exists, yet they let the agent initiate a paid subscription and obtain credentials. In a hostile prompt or confused-deputy scenario, this expands the blast radius from data retrieval to financial transactions and credential issuance.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The auto top-up endpoints allow enabling persistent automatic purchases when credits fall below a threshold, which exceeds ordinary lead-management behavior and creates ongoing financial authority. This is especially risky in agent workflows because a one-time action can authorize repeated future charges without per-transaction user review.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
python smb_api.py smbk_xxx GET /ai/keyword-status
  python smb_api.py smbk_xxx POST /email-schedules/15/trigger
  python smb_api.py smbk_xxx POST /filter-presets --body '{"name":"NY Bakeries","filters":{"positiveKeywords":["*bakery*","*cater*","*pastry*"],"stateInclude":"NY"}}'
  python smb_api.py smbk_xxx DELETE /filter-presets/42
  python smb_api.py smbk_xxx POST /purchase-credits --body '{"creditCount":500}'
  python smb_api.py smbk_xxx POST /subscription/change-plan --body '{"targetPlan":"growth"}'
  python smb_api.py smbk_xxx POST /subscription/cancel
Confidence
90% confidence
Finding
The tool is a generic API wrapper that allows arbitrary method/endpoint invocation, including destructive and billable operations such as deleting presets, triggering schedules, purchasing credits, and changing subscriptions. In an agent skill context, broad parameter freedom increases the risk of prompt-driven misuse or accidental execution of real-world side effects unless the caller enforces strict confirmation and endpoint allowlisting.

Credential Access

High
Category
Privilege Escalation
Content
"""Safely save base64-encoded export files with filename sanitization.

    Security measures:
    - os.path.basename() strips directory traversal components (e.g., ../../etc/passwd -> passwd)
    - Extension validated against allowlist (.csv, .json, .xlsx only)
    - Files written only to the designated output_dir, never to API-specified paths
    """
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
"""Safely save base64-encoded export files with filename sanitization.

    Security measures:
    - os.path.basename() strips directory traversal components (e.g., ../../etc/passwd -> passwd)
    - Extension validated against allowlist (.csv, .json, .xlsx only)
    - Files written only to the designated output_dir, never to API-specified paths
    """
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
"""Safely save base64-encoded export files with filename sanitization.

    Security measures:
    - os.path.basename() strips directory traversal components (e.g., ../../etc/passwd -> passwd)
    - Extension validated against allowlist (.csv, .json, .xlsx only)
    - Files written only to the designated output_dir, never to API-specified paths
    """
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
"""Safely save base64-encoded export files with filename sanitization.

    Security measures:
    - os.path.basename() strips directory traversal components (e.g., ../../etc/passwd -> passwd)
    - Extension validated against allowlist (.csv, .json, .xlsx only)
    - Files written only to the designated output_dir, never to API-specified paths
    """
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
93% confidence
Finding
The skill declares access to environment variables and relies on networked API calls, but it does not define an explicit tool scope such as allowed-tools or permissions. That weakens containment and reviewability, because an agent runtime may grant broader capabilities than intended when handling sensitive API keys, PII exports, and purchase-capable endpoints.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The manifest omits that the API can configure and trigger automated email schedules that distribute exported lead data to recipients. Because exports contain contact information and potentially PII, undisclosed automation increases the risk of silent mass dissemination beyond what a user expects from a lead-query skill.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The purchase-credits endpoint performs an off-session Stripe charge using a saved payment method, but the description does not prominently warn that invoking the endpoint can immediately bill the account. In an agent context, insufficient warning around immediate payment materially raises the risk of accidental unauthorized charges.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The auto top-up description mentions automatic purchases but does not provide a strong, explicit warning suitable for agent-mediated execution that enabling the setting authorizes future charges to the saved payment method. Without a conspicuous warning, users may not appreciate that this is a recurring financial action rather than a simple preference change.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script explicitly supports passing the API key as a positional command-line argument. On many systems, command-line arguments are exposed through process listings, shell history, job control logs, and telemetry, which can leak long-lived credentials to other local users or monitoring tools. In this skill context, the key grants access to lead data and purchase-related endpoints, making accidental disclosure more consequential.

External Transmission

Medium
Category
Data Exfiltration
Content
if method == "GET":
        resp = requests.get(url, headers=headers, params=params or {})
    elif method == "POST":
        resp = requests.post(url, headers=headers, json=body)
    elif method == "PATCH":
        resp = requests.patch(url, headers=headers, json=body)
    elif method == "PUT":
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
elif method == "PATCH":
        resp = requests.patch(url, headers=headers, json=body)
    elif method == "PUT":
        resp = requests.put(url, headers=headers, json=body)
    elif method == "DELETE":
        resp = requests.delete(url, headers=headers)
    else:
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Static analysis

No suspicious patterns detected.