T09 · Insecure Skill Coding Practices
- Location
commands/setup.md:202- Finding
Persistent Shell Command Injection Through API Key Storage
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill does lead prospecting as advertised, but its setup instructions handle API keys unsafely and can modify shell startup files in ways that create real local execution and persistence risk.
Review this skill before installing. Only run setup with API keys you generated yourself, avoid writing keys into shell profiles, prefer environment variables or a proper secret manager, and treat exported CSV lead data as sensitive. Before using Attio sync, confirm you are allowed to send and retain those contacts in that CRM. The package should be fixed to pass secrets as data, quote shell exports safely, pin dependencies, and neutralize CSV formulas.
commands/setup.md:202Persistent Shell Command Injection Through API Key Storage
commands/setup.md:75Arbitrary Python Code Injection During API Key Validation
scripts/prospector.py:369Spreadsheet Formula Injection in Exported Lead CSV Files
README.md:13Unpinned Global Installation of a Third-Party Dependency
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
2. Ask for your Apollo API key and validate it
3. Optionally ask for your Attio API key
4. Optionally set environment variables in your shell profile
5. Save keys securely to `~/.config/prospector/config.json` (chmod 600)
### Finding Leads
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
2. Ask for your Apollo API key and validate it
3. Optionally ask for your Attio API key
4. Optionally set environment variables in your shell profile
5. Save keys securely to `~/.config/prospector/config.json` (chmod 600)
### Finding Leads
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
2. Ask for your Apollo API key and validate it
3. Optionally ask for your Attio API key
4. Optionally set environment variables in your shell profile
5. Save keys securely to `~/.config/prospector/config.json` (chmod 600)
### Finding Leads
The README explicitly describes exporting personal contact data, including names, emails, locations, and LinkedIn URLs, to a CSV on the user's Desktop without any privacy, retention, or secure-handling guidance. That creates a real data-protection risk because Desktop files are easily exposed through backups, screen sharing, local multi-user access, or accidental onward sharing.
The README states that leads can be synced to Attio CRM, including company and people records, but does not warn that personal contact data will be transmitted to a third-party service. This is a genuine privacy/security concern because users may unknowingly send regulated or sensitive business-contact data to another processor without understanding the disclosure, retention, and access implications.
Creating persistent CRM people/company records from prospecting data introduces a real session/data persistence risk because imported personal data may remain in the CRM indefinitely and become accessible to broader teams or downstream integrations. In this skill context, the danger is increased because the workflow is expressly designed to collect and enrich contact records at scale, making over-retention and secondary use more likely.
If you configured Attio during setup, you'll be asked after each search if you want to sync the leads. This will:
- Create/update Companies in Attio (matched by domain to avoid duplicates)
- Create People linked to their companies
## CLI Usage
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
}
The file is automatically set to `chmod 600` (owner read/write only).
### Environment Variables (Recommended)
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
}
The file is automatically set to `chmod 600` (owner read/write only).
### Environment Variables (Recommended)
The skill invokes shell commands, reads environment variables, writes configuration and CSV files, and sends data to external services, but it declares no explicit tool scope or permissions. That makes the skill's operational reach opaque to the user and platform, increasing the risk of over-privileged execution or unexpected data access if the skill is run in an agent environment.
The skill sends user-provided ICP criteria and retrieved prospect/contact data to third-party services Exa, Apollo, and optionally Attio, but the description lacks an explicit privacy notice. This is dangerous because users may not understand that their search criteria and resulting lead data are being transmitted off-platform to external vendors, creating privacy, compliance, and data-handling risks.
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.
allowed-tools:
- Bash
- AskUserQuestion
- Write
- Read
---
The setup flow asks the user to paste API keys and then sends those secrets to third-party endpoints for validation, but it does not explicitly warn the user that their credentials will be transmitted over the network. Although validating a key against the vendor API is a legitimate pattern, the lack of clear disclosure reduces informed consent and increases the chance of unexpected secret exposure.
The skill offers to append plaintext API keys directly into shell startup files, which creates persistent local secret storage in locations that may be broadly read by local tools, backup systems, or accidentally shared dotfiles. The issue is compounded by the absence of an explicit warning that secrets will be written into startup scripts in plaintext.
The skill saves API keys in plaintext to ~/.config/prospector/config.json and only later mentions that keys are stored there, rather than warning beforehand that credentials will be written to disk. Even with chmod 600, plaintext storage can still expose secrets to local compromise, backups, endpoint tooling, or accidental exfiltration.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
try:
with httpx.Client(timeout=30) as client:
resp = client.post(
"https://api.exa.ai/search",
headers={"x-api-key": api_key, "Content-Type": "application/json"},
json={"query": "test", "numResults": 1},
)
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
try:
with httpx.Client(timeout=30) as client:
resp = client.post(
"https://api.exa.ai/search",
headers={"x-api-key": api_key, "Content-Type": "application/json"},
json={"query": "test", "numResults": 1},
)
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
try:
with httpx.Client(timeout=30) as client:
resp = client.post(
"https://api.exa.ai/search",
headers={"x-api-key": api_key, "Content-Type": "application/json"},
json={"query": "test", "numResults": 1},
)
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
try:
with httpx.Client(timeout=30) as client:
resp = client.post(
"https://api.apollo.io/api/v1/mixed_people/search",
headers={"x-api-key": api_key, "Content-Type": "application/json"},
json={"per_page": 1},
)
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
try:
with httpx.Client(timeout=30) as client:
resp = client.post(
"https://api.apollo.io/api/v1/mixed_people/search",
headers={"x-api-key": api_key, "Content-Type": "application/json"},
json={"per_page": 1},
)
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
try:
with httpx.Client(timeout=30) as client:
resp = client.post(
"https://api.apollo.io/api/v1/mixed_people/search",
headers={"x-api-key": api_key, "Content-Type": "application/json"},
json={"per_page": 1},
)
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
try:
with httpx.Client(timeout=30) as client:
resp = client.get(
"https://api.attio.com/v2/self",
headers={"Authorization": f"Bearer {api_key}"},
)
return resp.status_code == 200
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
try:
with httpx.Client(timeout=30) as client:
resp = client.get(
"https://api.attio.com/v2/self",
headers={"Authorization": f"Bearer {api_key}"},
)
return resp.status_code == 200
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
try:
with httpx.Client(timeout=30) as client:
resp = client.get(
"https://api.attio.com/v2/self",
headers={"Authorization": f"Bearer {api_key}"},
)
return resp.status_code == 200
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
try:
with httpx.Client(timeout=30) as client:
resp = client.get(
"https://api.attio.com/v2/self",
headers={"Authorization": f"Bearer {api_key}"},
)
return resp.status_code == 200
The workflow states that lead data is exported to CSV on the user's Desktop, but the user-facing description does not clearly warn about that local file write. This can cause unintended storage of potentially sensitive prospecting data in an exposed or synced location, especially on shared machines or systems with cloud-desktop sync enabled.
No suspicious patterns detected.