Back to skill

Security audit

Explorium AgentSource

Security checks for vulnerabilities and agentic risk

Overview

The skill’s prospecting purpose is coherent, but it needs review because it handles API keys and personal contact data with avoidable local exposure risks.

Review this skill before installing if you will process customer lists or personal contact data. Prefer setting EXPLORIUM_API_KEY outside the installer, avoid entering secrets where terminal output is logged, remove or protect /tmp/agentsource_*.json files after use, and require explicit consent before sending free-text query context or imported CSV records to Explorium.

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

T09 · Insecure Skill Coding Practices

Warning
Location
bin/agentsource.py:116
Finding
Predictable and Insufficiently Protected Temporary Files Expose Sensitive Prospect Data<![CDATA[ ## Vulnerability Details **File Location**: `bin/agentsource.py:116-124` **Vulnerability Type**: Predictable temporary-file creation with permissions inherited from the process umask **Risk Level**: Medium ### Vulnerable Code ```python def make_temp_path(command: str) -> pathlib.Path: ts = int(time.time()) return TEMP_DIR / f"agentsource_{ts}_{command}.json" def write_result(command: str, data: dict) -> pathlib.Path: path = make_temp_path(command) path.write_text(json.dumps(data, indent=2, default=str)) print(str(path)) return path ``` The same pattern is used for error files: ```python path = make_temp_path(f"{command}_error") path.write_text(json.dumps(data, indent=2)) ``` ### Technical Analysis The CLI writes imported CSV rows, fetched companies, prospect contact information, enrichment results, and event data directly to the shared `/tmp` directory. Filenames contain only a timestamp with one-second resolution and a known command name. The implementation does not: - Generate a cryptographically random filename. - Atomically reserve the destination before writing. - Explicitly set result-file permissions to `0600`. - Use a private temporary directory with permissions such as `0700`. - Remove result files after use. Consequently, file permissions depend on the invoking process's umask. With a common `022` umask, newly created files may be readable by other local users. Predictable names also permit collisions between invocations of the same command during the same second. Depending on operating-system temporary-file protections, pre-creation or link-based interference may also be possible. The README states that these files are cleaned up automatically by the operating system, but that does not provide timely confidentiality guarantees and does not prevent exposure while the files exist. ### Attack Path 1. An attacker with access to another local account monitors `/tmp` for names matching `agentsource_*_fetch.jso ...[truncated 1153 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create a private per-user temporary directory with mode `0700`. 2. Use `tempfile.NamedTemporaryFile(delete=False)` or `tempfile.mkstemp()` to atomically create unpredictable files. 3. Explicitly create every result file with mode `0600`, independent of the process umask. 4. Avoid second-resolution names and prevent concurrent commands from sharing a destination. 5. Add a cleanup command or configurable retention policy for files containing personal data. 6. Where possible, delete intermediate imported CSV JSON files immediately after matching. 7. Document the retention period and local exposure risk rather than relying solely on eventual operating-system cleanup. A hardened implementation should use an atomic file descriptor returned by `tempfile`, write through that descriptor, flush the data, and return only the resulting randomized path. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
setup.sh:81
Finding
Setup Script Displays the Explorium API Key in Plaintext<![CDATA[ ## Vulnerability Details **File Location**: `setup.sh:81-89` **Vulnerability Type**: Plaintext credential input and terminal disclosure **Risk Level**: Medium ### Vulnerable Code ```bash read -r -p "Enter your Explorium API key (or press Enter to skip): " api_key if [ -n "${api_key:-}" ]; then printf '{\n "api_key": "%s"\n}\n' \ "$api_key" > "$CONFIG_FILE" chmod 600 "$CONFIG_FILE" echo "[OK] API key saved to $CONFIG_FILE (mode 600, owner read-only)" echo "" echo "To also set it as an environment variable, add this to ~/.zshrc or ~/.bashrc:" echo " export EXPLORIUM_API_KEY='$api_key'" ``` ### Technical Analysis The interactive `read` command does not use silent input, so the API key is displayed while the user types it. The script subsequently prints the entire credential as part of an example environment-variable command. Although the saved configuration file is changed to mode `0600`, that protection does not address disclosure through: - Visible terminal input. - Terminal scrollback. - CI or automation logs. - Shell-session recording. - Remote support or screen-sharing sessions. - Shoulder surfing. The setup documentation correctly warns users not to disclose the key in chat, but the installer itself unnecessarily reproduces the secret in terminal output. The network use of the API key is necessary for the declared Explorium functionality. Displaying it in plaintext is not necessary and exceeds the minimum exposure required to configure the Skill. ### Attack Path 1. A user runs `setup.sh` and enters an Explorium API key at the prompt. 2. The key appears visibly as it is entered. 3. The script prints the complete key again in an `export EXPLORIUM_API_KEY='...'` example. 4. An observer, terminal logger, CI log collector, or session-recording system captures the output. 5. The attacker retrieves the key and uses it to authenticate directly to the Explorium API. ### Impact Assessment A disclosed key may permi ...[truncated 541 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Read interactive secrets silently: ```bash read -r -s -p "Enter your Explorium API key (or press Enter to skip): " api_key printf '\n' ``` 2. Never print the value of `$api_key`. 3. Replace the generated export example with a placeholder: ```bash echo " export EXPLORIUM_API_KEY='<your-key>'" ``` 4. Prefer secure environment injection or an operating-system credential store over interactive plaintext entry. 5. Warn users that command-line arguments such as `config --api-key <key>` may be visible in process listings or shell history. 6. Consider accepting the credential through standard input or a protected file descriptor. 7. Ensure installer logs redact values matching the credential input. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:124
Finding
Workflow Examples Can Transmit User Query Text Without an Explicit Consent Step<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:124-135` **Vulnerability Type**: Conflicting privacy instructions for optional remote query logging **Risk Level**: Medium ### Vulnerable Code ```bash RESULT=$(python3 "$CLI" autocomplete \ --entity-type businesses \ --field linkedin_category \ --query "software" \ --semantic \ --plan-id "$PLAN_ID" \ --call-reasoning "$QUERY") cat "$RESULT" ``` Additional workflow examples repeat the same `--call-reasoning "$QUERY"` pattern at `SKILL.md:323`, `330`, `342`, `359`, and `372`. Earlier instructions correctly state: ```markdown Privacy note: `--call-reasoning` sends the user's query text to `api.explorium.ai` as part of the request metadata. Only pass it if the user has consented to this. ``` ### Technical Analysis The implementation sends `call_reasoning` to Explorium only when the command-line option is supplied, which is an appropriate opt-in mechanism at the CLI level. However, the Skill instructions provide several normal workflow examples in which the option is already present. An AI agent following these examples may treat `--call-reasoning` as part of the standard command rather than first asking the user for explicit permission. This conflicts with the earlier privacy note and weakens the intended consent boundary. The natural-language query can contain confidential information beyond standardized search filters, such as: - Named individuals or accounts. - Private target lists. - Internal sales strategy. - Product plans or acquisition interests. - Customer or partner information. - Free-form contextual details not needed for the API operation. The actual search filters and entity identifiers are necessary for the declared remote prospecting functionality. Sending the complete natural-language query for server-side logging is optional and is not required to perform autocomplete, matching, fetching, enrichment, or event retrieval. ### Attack Path 1. A user submits a natura ...[truncated 906 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `--call-reasoning "$QUERY"` from every default workflow example. 2. Place the option only in a dedicated conditional consent branch. 3. Require an explicit affirmative response before enabling query logging. 4. Do not infer consent merely because the user requested an API-backed search. 5. Store a per-workflow consent flag and omit the argument unless that flag is true. 6. Explain the purpose, destination, and likely retention of the query before requesting consent. 7. Offer a privacy-preserving alternative that sends only required filters and identifiers. 8. Consider minimizing the logged value even after consent, for example by sending a short non-sensitive operation label rather than the complete user request. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
plugin.json:23
Finding
Plugin Privacy Metadata Omits Company and Contact Records Sent for Matching<![CDATA[ ## Vulnerability Details **File Location**: `plugin.json:23-28` **Vulnerability Type**: Incomplete sensitive-data transmission disclosure **Risk Level**: Low ### Vulnerable Code ```json "data_privacy": { "remote_endpoints": ["https://api.explorium.ai/v1/"], "data_sent": "API key (header), search filters, entity IDs. Free-text call_reasoning is opt-in only.", "local_storage": [ "~/.agentsource/config.json (API key, mode 600, owner read-only)", "/tmp/agentsource_*.json (result data, auto-cleaned by OS)" ] } ``` The implementation additionally sends mapped company and contact records: ```python body: dict = {"businesses_to_match": batch} raw = _request(api_key, "POST", "businesses/match", body=body) ``` ```python body: dict = {"prospects_to_match": batch} raw = _request(api_key, "POST", "prospects/match", body=body) ``` Mapped prospect fields can include names, employers, email addresses, and LinkedIn identifiers. ### Technical Analysis The README discloses that company and contact records are sent during matching, but the primary `plugin.json` metadata states only that API keys, filters, entity IDs, and optional query reasoning are transmitted. Permission review interfaces and automated installation systems may rely on `plugin.json` rather than the README. As a result, users may authorize the Skill without being informed through the primary metadata that personal or organization-specific records from imported CSV files can be transmitted. The transmission itself is necessary when the user explicitly invokes matching. The vulnerability is the inconsistent disclosure of its data scope, which undermines informed consent and least-data expectations. ### Attack Path 1. A user reviews or approves the Skill based on `plugin.json`. 2. The metadata does not state that company or contact records are transmitted. 3. The user imports an existing CSV and invokes `match-business` or `match-prospect`. 4. The CLI extracts mapped fields from e ...[truncated 637 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Expand `data_privacy.data_sent` to explicitly include: - Company names and domains. - Contact names and employers. - Email addresses. - LinkedIn identifiers. - Other fields selected through `--column-map`. 2. State that these fields are sent when `match-business` or `match-prospect` is invoked. 3. Keep `plugin.json`, README, and `SKILL.md` privacy disclosures synchronized. 4. Before processing an imported CSV, present the selected column mapping and remote destination to the user. 5. Require confirmation before transmitting mapped personal data from a local file. 6. Minimize matching payloads to only fields needed for a reliable match. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (16)

Session Persistence

Medium
Category
Rogue Agent
Content
**Option A — Environment variable** (recommended for persistent shell setups):
```bash
export EXPLORIUM_API_KEY=your_api_key_here
# Add to ~/.zshrc or ~/.bashrc to persist across sessions
```

**Option B — CLI config command** (saves to `~/.agentsource/config.json`, mode 600):
Confidence
90% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
**Option A — Environment variable** (recommended for persistent shell setups):
```bash
export EXPLORIUM_API_KEY=your_api_key_here
# Add to ~/.zshrc or ~/.bashrc to persist across sessions
```

**Option B — CLI config command** (saves to `~/.agentsource/config.json`, mode 600):
Confidence
90% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
## CLI Reference

All commands write results to `/tmp/agentsource_<timestamp>_<command>.json` and print only the file path to stdout.

### API Key Config
```bash
Confidence
84% confidence
Finding
Writing all API responses to predictable `/tmp/agentsource_*.json` files can expose sensitive prospecting data, contact information, enrichment results, or matching inputs to other local users or processes if file permissions are not tightly controlled. In a multi-user machine, shared temp environment, or agent runtime with other tools watching `/tmp`, this creates a realistic local data leakage risk, especially because the skill handles business and contact datasets at scale.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill instructs the agent to use shell, read/write files, access environment variables, and make networked API calls, but it does not declare any explicit tool scope such as permissions or allowed-tools. This creates an overprivileged execution surface where the runtime may permit broader capabilities than intended, increasing the blast radius if the skill is misused or the surrounding agent is compromised.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The `to_csv` command writes the full flattened entity dataset to a user-specified CSV path, which can include prospect or business data, but the code provides no confirmation prompt or user-facing warning at the point of export. While the CLI has general comments about temp-file behavior, there is no explicit disclosure here that potentially sensitive fetched/enriched data will be persisted outside `/tmp`.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill explicitly supports enrichment of prospect records with personal email addresses and phone numbers, but the reference provides no privacy, consent, lawful-basis, or sensitive-data handling guidance. In a B2B prospecting skill, this omission can lead users or downstream agents to collect and export personal contact data in ways that violate privacy policies, internal governance, or applicable regulations.

External Transmission

Medium
Category
Data Exfiltration
Content
#   ~/.agentsource/config.json          — API key storage (mode 600, only if you choose to save it)
#
# Nothing is sent to any network during setup. The API key is only used when
# you run CLI commands that call https://api.explorium.ai/v1/.

PLUGIN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
INSTALL_DIR="$HOME/.agentsource"
Confidence
60% 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
#   ~/.agentsource/config.json          — API key storage (mode 600, only if you choose to save it)
#
# Nothing is sent to any network during setup. The API key is only used when
# you run CLI commands that call https://api.explorium.ai/v1/.

PLUGIN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
INSTALL_DIR="$HOME/.agentsource"
Confidence
60% 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
#   ~/.agentsource/config.json          — API key storage (mode 600, only if you choose to save it)
#
# Nothing is sent to any network during setup. The API key is only used when
# you run CLI commands that call https://api.explorium.ai/v1/.

PLUGIN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
INSTALL_DIR="$HOME/.agentsource"
Confidence
60% 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
#   ~/.agentsource/config.json          — API key storage (mode 600, only if you choose to save it)
#
# Nothing is sent to any network during setup. The API key is only used when
# you run CLI commands that call https://api.explorium.ai/v1/.

PLUGIN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
INSTALL_DIR="$HOME/.agentsource"
Confidence
60% 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
#   ~/.agentsource/config.json          — API key storage (mode 600, only if you choose to save it)
#
# Nothing is sent to any network during setup. The API key is only used when
# you run CLI commands that call https://api.explorium.ai/v1/.

PLUGIN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
INSTALL_DIR="$HOME/.agentsource"
Confidence
60% 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
#   ~/.agentsource/config.json          — API key storage (mode 600, only if you choose to save it)
#
# Nothing is sent to any network during setup. The API key is only used when
# you run CLI commands that call https://api.explorium.ai/v1/.

PLUGIN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
INSTALL_DIR="$HOME/.agentsource"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Session Persistence

Medium
Category
Rogue Agent
Content
echo "[OK] Python $PYTHON_VERSION"

# ---------------------------------------------------------------------------
# 2. Create directory structure
# ---------------------------------------------------------------------------
mkdir -p "$BIN_DIR"
echo "[OK] Created $BIN_DIR"
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
if [[ "${save_key:-}" =~ ^[Yy]$ ]]; then
        printf '{\n  "api_key": "%s"\n}\n' \
            "$EXPLORIUM_API_KEY" > "$CONFIG_FILE"
        chmod 600 "$CONFIG_FILE"
        echo "[OK] API key saved to $CONFIG_FILE (mode 600, owner read-only)"
    fi
else
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
if [[ "${save_key:-}" =~ ^[Yy]$ ]]; then
        printf '{\n  "api_key": "%s"\n}\n' \
            "$EXPLORIUM_API_KEY" > "$CONFIG_FILE"
        chmod 600 "$CONFIG_FILE"
        echo "[OK] API key saved to $CONFIG_FILE (mode 600, owner read-only)"
    fi
else
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The script reads an API key from terminal input and stores it locally, then prints a shell export command containing the secret inline. This can expose the key via shoulder-surfing, terminal scrollback, shell history if copied, screenshots, or logs collected by terminal/session recording tools.

Static analysis

No suspicious patterns detected.