Back to skill

Security audit

Hubspot Suite

Security checks for vulnerabilities and agentic risk

Overview

This is a real HubSpot helper, but it needs review because it requests powerful CRM access and includes unsafe script patterns that could expose tokens or change data unexpectedly.

Review before installing. Use a minimally scoped HubSpot private-app token, avoid setting HUBSPOT_BASE_URL except to the official HubSpot API, do not pass untrusted object or property names to the scripts, and run mutating actions such as imports, merges, deletes, workflow changes, and property edits only after a backup or dry run.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/find-duplicates.sh:9
Finding
User-Controlled Property Name Is Interpolated into Python Source Code<![CDATA[ ## Vulnerability Details **File Location**: `scripts/find-duplicates.sh`, lines 9 and 26–32 **Vulnerability Type**: Python source injection leading to arbitrary local command execution **Risk Level**: High ### Vulnerable Code ```bash PROPERTY="${2:?Usage: find-duplicates.sh <object-type> <property>}" ``` ```bash RESULTS=$(echo "$RESP" | python3 -c " import sys, json data = json.load(sys.stdin) for r in data.get('results', []): val = r.get('properties', {}).get('$PROPERTY', '') if val: print(f\"{r['id']}|{val}\") " 2>/dev/null) ``` ### Technical Analysis The `PROPERTY` command-line argument is inserted directly into a Python program supplied to `python3 -c`. Although the shell variable is expanded inside a double-quoted shell string, it becomes part of executable Python source code. An attacker-controlled value containing Python string delimiters and additional Python statements can terminate the intended string expression and inject new statements. The injected Python executes with the same operating-system privileges and environment as the shell script. This is not required for duplicate detection. The property name should be passed as data through a positional argument or environment variable rather than used to generate Python source. The same value is also included in a HubSpot API URL without explicit validation or URL encoding. That can alter query semantics, although the fixed HTTPS origin prevents it from independently redirecting the bearer token to another host. ### Attack Path 1. An attacker influences the property argument passed to `find-duplicates.sh`, such as through an automated Agent task or an untrusted user request. 2. The script assigns the value to `PROPERTY` without validating it. 3. The value is interpolated into the Python expression: `get('$PROPERTY', '')`. 4. A malicious value closes the intended Python string and introduces additional Python statements, such as importing an operating-system exec ...[truncated 917 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Do not interpolate command-line values into Python source. Pass the property as a separate argument: ```bash RESULTS=$(printf '%s' "$RESP" | python3 -c ' import json import sys prop = sys.argv[1] data = json.load(sys.stdin) for record in data.get("results", []): value = record.get("properties", {}).get(prop, "") if value: print(f"{record['id']}|{value}") ' "$PROPERTY") ``` Apply strict validation before using the object type or property: ```bash case "$OBJECT_TYPE" in contacts|companies|deals|tickets) ;; *) echo "Unsupported object type" >&2 exit 1 ;; esac if [[ ! "$PROPERTY" =~ ^[A-Za-z0-9_]+$ ]]; then echo "Invalid property name" >&2 exit 1 fi ``` Additional hardening should include: 1. Construct query strings with URL encoding instead of direct concatenation. 2. Avoid suppressing all Python errors with `2>/dev/null`, because doing so hides malformed input and exploitation attempts. 3. Validate API responses and fail explicitly when HubSpot returns an error object. 4. Run shell static analysis and add tests using quotes, newlines, separators, and other hostile property values. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/hs-api.sh:9
Finding
Bearer Token Can Be Forwarded to an Arbitrary Configured API Host<![CDATA[ ## Vulnerability Details **File Location**: `scripts/hs-api.sh`, lines 9, 113–117, and 252 **Vulnerability Type**: Credential disclosure through an unrestricted API base URL **Risk Level**: High ### Vulnerable Code ```bash HUBSPOT_BASE_URL="${HUBSPOT_BASE_URL:-https://api.hubapi.com}" ``` ```bash local curl_cmd=( "curl" "-s" "-w" "%{http_code}" "-H" "Authorization: Bearer $HUBSPOT_ACCESS_TOKEN" "-H" "Content-Type: application/json" "-D" "$headers_file" "-X" "$method" ) ``` ```bash local url="$HUBSPOT_BASE_URL$endpoint" ``` The override is also documented in `SKILL.md`: ```bash export HUBSPOT_BASE_URL="https://api.hubapi.com" # Override for testing ``` ### Technical Analysis `HUBSPOT_BASE_URL` is accepted from the process environment without validating its scheme, hostname, port, user-information component, or destination. The helper then attaches `HUBSPOT_ACCESS_TOKEN` to every request made to the resulting URL. Consequently, anyone who can influence the environment or setup instructions can point the helper at a non-HubSpot server. The script will transmit the production bearer token in the `Authorization` header to that server. It also accepts plaintext HTTP URLs, allowing interception over an untrusted network. A testing override can be legitimate, but forwarding a production credential to an unrestricted host exceeds the minimum privilege needed for normal HubSpot management. ### Attack Path 1. An attacker influences the runtime environment, a wrapper script, shell configuration, CI configuration, or instructions used to launch the Skill. 2. `HUBSPOT_BASE_URL` is set to an attacker-controlled HTTP or HTTPS endpoint. 3. The user or Agent invokes `hs-api.sh` for an otherwise legitimate HubSpot operation. 4. The script concatenates the attacker-controlled base URL with the supplied endpoint. 5. The generated `curl` request includes `Authorization: Bearer $HUBSPOT_ACCESS_TOKEN`. 6. The attacker-controlled serv ...[truncated 866 chars]
Remediation
<![CDATA[ ## Remediation Suggestions For production use, remove the override and use a fixed HubSpot API origin: ```bash readonly HUBSPOT_BASE_URL="https://api.hubapi.com" ``` If an override is operationally necessary, parse and validate it before making any request: 1. Require the `https` scheme. 2. Allowlist exact trusted hostnames. 3. Reject embedded credentials, fragments, unexpected ports, and malformed URLs. 4. Never send production tokens to localhost, private-network addresses, or testing servers. 5. Require a separate test token when a non-production endpoint is selected. 6. Display the validated destination before sending a credential when operating in test mode. For example: ```bash case "$HUBSPOT_BASE_URL" in https://api.hubapi.com) ;; *) echo "Refusing to send a HubSpot token to an untrusted API origin" >&2 exit 1 ;; esac ``` Also constrain endpoints to relative paths: ```bash if [[ ! "$endpoint" =~ ^/[A-Za-z0-9._~/?=&%:-]+$ ]] || [[ "$endpoint" == //* ]]; then echo "Invalid API endpoint" >&2 exit 1 fi ``` Use narrowly scoped private-app tokens, rotate the affected token if it may have been used with an untrusted base URL, and review HubSpot API logs for anomalous access. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
references/data-quality.md:47
Finding
Predictable Shared Temporary File Stores CRM Personally Identifiable Information<![CDATA[ ## Vulnerability Details **File Location**: `references/data-quality.md`, lines 47–81 **Vulnerability Type**: Insecure temporary-file creation and sensitive local data exposure **Risk Level**: Medium ### Vulnerable Code ```bash # Get all records local records_file="/tmp/hubspot_${object_type}_${match_field}.txt" curl -X POST "https://api.hubapi.com/crm/v3/objects/$object_type/search" \ -H "Authorization: Bearer $HUBSPOT_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d "{ \"properties\": [\"$match_field\", \"firstname\", \"lastname\", \"name\"], \"filters\": [{\"propertyName\": \"$match_field\", \"operator\": \"HAS_PROPERTY\"}], \"limit\": 1000 }" | \ jq -r --arg field "$match_field" '.results[] | [.id, .properties[$field], (.properties.firstname // .properties.name), .properties.lastname] | @csv' > "$records_file" # Find potential matches echo "Potential duplicate groups:" case "$match_field" in "email") # Email variations (gmail.com vs googlemail.com, etc.) cat "$records_file" | while IFS=',' read -r id email first last; do normalized=$(echo "$email" | sed 's/googlemail.com/gmail.com/g' | tr '[:upper:]' '[:lower:]') echo "$id,$normalized,$first,$last" done | sort -k2 | uniq -D -f1 ;; "name"|"company") # Name variations (Inc vs Inc. vs Incorporated) cat "$records_file" | while IFS=',' read -r id name first last; do normalized=$(echo "$name" | sed -e 's/\bInc\.\?/Inc/g' -e 's/\bCorp\.\?/Corp/g' -e 's/\bLLC\b/LLC/g' | tr '[:upper:]' '[:lower:]') echo "$id,$normalized,$first,$last" done | sort -k2 | uniq -D -f1 ;; esac rm "$records_file" ``` ### Technical Analysis The documented workflow writes HubSpot record identifiers, email addresses, first names, last names, and company names to a predictable path under the system-wide `/tmp` directory. The file is created with ord ...[truncated 1954 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Create the temporary file atomically with restrictive permissions and guarantee cleanup: ```bash umask 077 local records_file records_file=$(mktemp "${TMPDIR:-/tmp}/hubspot-records.XXXXXX") || { echo "Unable to create secure temporary file" >&2 return 1 } trap 'rm -f -- "$records_file"' RETURN ``` For compatibility where a `RETURN` trap is unsuitable, run the operation in a subshell and use an `EXIT` trap: ```bash ( umask 077 records_file=$(mktemp "${TMPDIR:-/tmp}/hubspot-records.XXXXXX") || exit 1 trap 'rm -f -- "$records_file"' EXIT # Fetch and process records here. ) ``` Additional controls should include: 1. Validate `object_type` against an explicit allowlist. 2. Validate `match_field` against permitted HubSpot property names. 3. Avoid retaining raw CRM data longer than necessary; process the response as a stream where practical. 4. Use a private runtime directory with mode `0700` when handling large or repeated exports. 5. Check the `curl` and `jq` exit statuses before processing the file. 6. Document that exported CRM files contain sensitive data and must not be placed in shared directories. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (349)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The documentation implies broad CRM management and reporting features, while the underlying behavior may only read from a single endpoint and omit promised filtering logic. This is a true security issue because deceptive or inaccurate operational claims undermine informed consent, least privilege, and safe tool selection.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documentation implies broad CRM management and reporting features, while the underlying behavior may only read from a single endpoint and omit promised filtering logic. This is a true security issue because deceptive or inaccurate operational claims undermine informed consent, least privilege, and safe tool selection.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documentation implies broad CRM management and reporting features, while the underlying behavior may only read from a single endpoint and omit promised filtering logic. This is a true security issue because deceptive or inaccurate operational claims undermine informed consent, least privilege, and safe tool selection.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The documentation implies broad CRM management and reporting features, while the underlying behavior may only read from a single endpoint and omit promised filtering logic. This is a true security issue because deceptive or inaccurate operational claims undermine informed consent, least privilege, and safe tool selection.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The documentation implies broad CRM management and reporting features, while the underlying behavior may only read from a single endpoint and omit promised filtering logic. This is a true security issue because deceptive or inaccurate operational claims undermine informed consent, least privilege, and safe tool selection.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documentation implies broad CRM management and reporting features, while the underlying behavior may only read from a single endpoint and omit promised filtering logic. This is a true security issue because deceptive or inaccurate operational claims undermine informed consent, least privilege, and safe tool selection.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The documentation implies broad CRM management and reporting features, while the underlying behavior may only read from a single endpoint and omit promised filtering logic. This is a true security issue because deceptive or inaccurate operational claims undermine informed consent, least privilege, and safe tool selection.

Vague Triggers

High
Confidence
97% confidence
Finding
The description explicitly says to use the skill for 'ANY HubSpot-related task,' creating an excessively broad activation scope. In an agent system, that increases the likelihood the skill is selected for sensitive workflows involving CRM data, exports, administration, or destructive changes even when a narrower, safer skill would be more appropriate.

Ae1

High
Category
analysis-evasion
Content
./scripts/hs-api.sh GET /crm/v3/objects/contacts
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
./scripts/hs-api.sh GET /crm/v3/objects/contacts
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Credential Access

High
Category
Privilege Escalation
Content
Load with:
```bash
set -a && source .env && set +a
```

## Security Best Practices
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
1. **Private App Setup**
   - Settings → Integrations → Private Apps
   - Select required scopes
   - Generate access token
   - Test API connection

2. **Webhook Configuration**
Confidence
89% confidence
Finding
The phrase 'Generate access token' refers to creation of a live authentication credential for HubSpot APIs. While not itself malicious, in a skill designed to help with HubSpot administration, this materially raises credential-handling risk because users may be encouraged to create and provide a token without adjacent safety constraints, enabling unauthorized API access if the token is disclosed.

External Script Fetching

High
Category
Supply Chain
Content
URL="${BASE}/crm/v3/objects/${OBJECT_TYPE}?limit=100&properties=${PROPERTY}"
  [ -n "$AFTER" ] && URL="${URL}&after=${AFTER}"
  
  RESP=$(curl -s -H "Authorization: Bearer ${TOKEN}" "$URL")
  
  RESULTS=$(echo "$RESP" | python3 -c "
import sys, json
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Credential Access

High
Category
Privilege Escalation
Content
$0 PATCH /crm/v3/objects/contacts/12345 '{"properties": {"firstname": "John"}}'

Environment Variables:
  HUBSPOT_ACCESS_TOKEN  Your HubSpot access token (required)
  HUBSPOT_BASE_URL      Base API URL (default: https://api.hubapi.com)
  DEBUG                 Set to 1 for verbose output
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
$0 PATCH /crm/v3/objects/contacts/12345 '{"properties": {"firstname": "John"}}'

Environment Variables:
  HUBSPOT_ACCESS_TOKEN  Your HubSpot access token (required)
  HUBSPOT_BASE_URL      Base API URL (default: https://api.hubapi.com)
  DEBUG                 Set to 1 for verbose output
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
$0 PATCH /crm/v3/objects/contacts/12345 '{"properties": {"firstname": "John"}}'

Environment Variables:
  HUBSPOT_ACCESS_TOKEN  Your HubSpot access token (required)
  HUBSPOT_BASE_URL      Base API URL (default: https://api.hubapi.com)
  DEBUG                 Set to 1 for verbose output
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
$0 PATCH /crm/v3/objects/contacts/12345 '{"properties": {"firstname": "John"}}'

Environment Variables:
  HUBSPOT_ACCESS_TOKEN  Your HubSpot access token (required)
  HUBSPOT_BASE_URL      Base API URL (default: https://api.hubapi.com)
  DEBUG                 Set to 1 for verbose output
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill declares shell and network-capable behaviors but does not define any explicit tool scope or permission boundaries. In an agent environment, this can allow broader-than-expected execution and outbound access, increasing the blast radius if the skill is invoked on sensitive tasks or with untrusted inputs.

External Transmission

Medium
Category
Data Exfiltration
Content
### 2. Basic API Test
```bash
curl -H "Authorization: Bearer $HUBSPOT_ACCESS_TOKEN" \
  "https://api.hubapi.com/crm/v3/objects/contacts?limit=1"
```
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The documentation includes data-affecting operations such as imports, merges, associations, and activity logging without warnings about irreversibility, side effects, or validation steps. In a CRM context this is especially dangerous because mistakes can corrupt customer records, trigger automations, or alter sales/service histories at scale.

External Transmission

Medium
Category
Data Exfiltration
Content
### Associate Contact with Company
```bash
curl -X PUT "https://api.hubapi.com/crm/v3/objects/contacts/CONTACT_ID/associations/companies/COMPANY_ID/1" \
  -H "Authorization: Bearer $HUBSPOT_ACCESS_TOKEN"
```
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
DEAL_ID=$(echo $DEAL_RESPONSE | jq -r '.id')

# 2. Associate with company
curl -X PUT "https://api.hubapi.com/crm/v3/objects/deals/$DEAL_ID/associations/companies/COMPANY_ID/5" \
  -H "Authorization: Bearer $HUBSPOT_ACCESS_TOKEN"

# 3. Associate with decision maker
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
### 3. Test Authentication
```bash
curl -H "Authorization: Bearer $HUBSPOT_ACCESS_TOKEN" \
  "https://api.hubapi.com/crm/v3/objects/contacts?limit=1"
```
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The documentation includes a write test that performs a real POST to create a contact in HubSpot using live credentials, but it does not clearly warn that this modifies production data. In an admin/integration skill context, users may copy-paste commands directly, causing unintended record creation, data pollution, or triggering downstream automations and notifications.

External Transmission

Medium
Category
Data Exfiltration
Content
### Test Scopes
```bash
# Test contact read
curl -H "Authorization: Bearer $HUBSPOT_ACCESS_TOKEN" \
  "https://api.hubapi.com/crm/v3/objects/contacts?limit=1"

# Test contact write
Confidence
60% 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.