Back to skill

Security audit

Fix Lifecycle Stages

Security checks for vulnerabilities and agentic risk

Overview

This HubSpot cleanup skill has a legitimate purpose, but its executable script can bulk change CRM records to Lead without adequate safeguards or matching the more careful instructions in the skill text.

Review and modify this skill before using it on a real HubSpot portal. Run only the audit first, verify the target account and record counts, use a narrowly scoped token or sandbox, export record-level backups, and require an explicit apply step before any lifecycle-stage changes or workflow activation.

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
scripts/execute.py:147
Finding

Context-Blind Bulk Lifecycle Mutation Contradicts the Documented Assignment Policy

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/execute.py:38
Finding

High-Volume Non-Transactional CRM Updates Lack Adequate Execution Safeguards

Content
View full analysis
SAFETY_THRESHOLD: print(f" SAFETY: Total exceeds threshold ({SAFETY_THRESHOLD:,}). Aborting.") continue if all_ids: updated, failed = clear_then_set(obj_type, all_ids, obj_label.lower()) print(f" Done. {updated:,} {obj_label.lower()} set to Lead. " f"{len(failed)} failed.") ``` ### Technical Analysis The script performs destructive updates immediately when the module is executed. It has no dry-run default, interactive confirmation, HubSpot tenant verification, or backup of ea ...[truncated 2253 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (22)

Tainted flow: 'HEADERS' from os.environ (line 31, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/after.py (reported line 66)May include surrounding context.

python
"properties": ["lifecyclestage"],
        "limit": 1,
    }
    resp = requests.post(
        f"{BASE}/crm/v3/objects/{object_type}/search",
        headers=HEADERS, json=body,
    )

Tainted flow: 'HEADERS' from os.environ (line 33, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/before.py (reported line 68)May include surrounding context.

python
"properties": ["lifecyclestage"],
        "limit": 1,
    }
    resp = requests.post(
        f"{BASE}/crm/v3/objects/{object_type}/search",
        headers=HEADERS, json=body,
    )

Tainted flow: 'HEADERS' from os.environ (line 37, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/execute.py (reported line 68)May include surrounding context.

python
body["after"] = after

        for attempt in range(MAX_RETRIES):
            resp = requests.post(
                f"{BASE}/crm/v3/objects/{object_type}/search",
                headers=HEADERS, json=body,
            )

Tainted flow: 'HEADERS' from os.environ (line 37, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/execute.py (reported line 108)May include surrounding context.

python
}

        for attempt in range(MAX_RETRIES):
            resp = requests.post(
                f"{BASE}/crm/v3/objects/{object_type}/batch/update",
                headers=HEADERS, json=body,
            )

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents an active remediation skill: it should update HubSpot records and create workflows to prevent future issues. The supplied code only queries HubSpot for counts of lifecycle stages on contacts and companies, checks whether disallowed stages or empty values remain, exports results to CSV, and prints PASS/FAIL plus manual workflow recommendations. There are no API calls that update objects or create workflows—only search/read operations. Therefore the actual behavior is materially narrower and different from the declared purpose.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared purpose promises corrective and preventive actions: updating missing lifecycle stages, repairing invalid stages, and creating workflows to prevent recurrence. However, this code chunk is a 'before state' audit script. It makes read-only search requests to HubSpot, aggregates counts, outputs a CSV report, and notes that another execute script would handle changes. Because the actual behavior is limited to auditing and reporting, the description materially overstates what this supplied code chunk does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The code does partially align with the description in that it fixes contacts and companies with missing or disallowed lifecycle stages using the HubSpot API. However, the declared description is broader and claims an additional capability the code does not perform: creating prevention workflows. The script only outputs instructions suggesting that a human create workflows afterward. Also, the stated purpose implies ensuring appropriate lifecycle stages generally, but the actual implementation applies a single hardcoded remediation—moving all matched records to Lead. That is a materially narrower and more specific behavior than the description suggests.

Content

No source excerpt is available for this finding.

Anti-Refusal Statement

High
Category
Anti-Refusal
Confidence
80% confidence
Finding

Skill instructs the agent to omit warnings, disclaimers, or ethical commentary. Stripping safety caveats hides risk from the user and is a common jailbreak preamble.

Content

Scanner excerpt · SKILL.md (reported line 37)May include surrounding context.

md
A direct set to an earlier stage will be **silently rejected** — no error, no warning, the value simply does not change. This is the single most common gotcha when fixing lifecycle stages.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/after.py (reported line 27)May include surrounding context.

python
from dotenv import load_dotenv

# ── Configuration ────────────────────────────────────────────────
load_dotenv(os.path.join(os.path.dirname(__file__), "..", ".env"))

TOKEN = os.environ["HUBSPOT_ACCESS_TOKEN"]
BASE = "https://api.hubapi.com"

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/before.py (reported line 29)May include surrounding context.

python
from dotenv import load_dotenv

# ── Configuration ────────────────────────────────────────────────
load_dotenv(os.path.join(os.path.dirname(__file__), "..", ".env"))

TOKEN = os.environ["HUBSPOT_ACCESS_TOKEN"]
BASE = "https://api.hubapi.com"

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/execute.py (reported line 33)May include surrounding context.

python
from dotenv import load_dotenv

# ── Configuration ────────────────────────────────────────────────
load_dotenv(os.path.join(os.path.dirname(__file__), "..", ".env"))

TOKEN = os.environ["HUBSPOT_ACCESS_TOKEN"]
BASE = "https://api.hubapi.com"

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill includes code and instructions that use environment-sourced credentials and make live HubSpot API calls, yet it declares no explicit tool scope or permission boundaries. In an agent environment, missing scope declarations can allow unintended access to network, secrets, or write-capable tooling without clear operator review.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill instructs bulk modification of CRM lifecycle stages and creation of prevention workflows in a live system without strong warnings, dry-run guidance, rollback planning, or approval checkpoints. In a production CRM, this can cause large-scale unintended data changes, reporting corruption, and workflow side effects if used incautiously.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This code reads HUBSPOT_ACCESS_TOKEN from the environment and uses it to authenticate requests to HubSpot, which is a sensitive credential access plus network transmission of account data. While the module docstring describes the script's functional purpose, it does not disclose that it will use an access token and make live API calls to an external service.

Content

No source excerpt is available for this finding.

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

python
"properties": ["lifecyclestage"],
        "limit": 1,
    }
    resp = requests.post(
        f"{BASE}/crm/v3/objects/{object_type}/search",
        headers=HEADERS, json=body,
    )

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

python
"properties": ["lifecyclestage"],
        "limit": 1,
    }
    resp = requests.post(
        f"{BASE}/crm/v3/objects/{object_type}/search",
        headers=HEADERS, json=body,
    )

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

python
"properties": ["lifecyclestage"],
        "limit": 1,
    }
    resp = requests.post(
        f"{BASE}/crm/v3/objects/{object_type}/search",
        headers=HEADERS, json=body,
    )

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
82% confidence
Finding

The documentation states disallowed stages include '(empty)' and describes a general clear-then-set fix for such records. However, the code uses the search operator 'NOT_HAS_PROPERTY', which matches records lacking the property rather than necessarily all records whose lifecycle stage is empty, creating a contradiction between stated intent and implemented selection logic.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
89% confidence
Finding

This script performs bulk modifications of CRM records and can reset lifecycle stages for large numbers of contacts and companies with minimal operator friction. In the skill context, that is more dangerous because a mistaken run, bad assumptions about 'disallowed' stages, or incomplete empty-stage detection can cause high-impact integrity damage to production CRM data.

Content

Scanner excerpt · scripts/execute.py (reported line 108)May include surrounding context.

python
}

        for attempt in range(MAX_RETRIES):
            resp = requests.post(
                f"{BASE}/crm/v3/objects/{object_type}/batch/update",
                headers=HEADERS, json=body,
            )

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest description says the skill 'creates prevention workflows to stop future gaps,' implying automated workflow creation as part of the skill's behavior. In this file, the code only outputs human-readable instructions to create two HubSpot workflows and performs no API calls or automation for workflow creation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The script writes after_fix_lifecycle.csv and later opens it in write mode, which can create or overwrite a local file. Although the docstring mentions a CSV audit trail and the script prints the output path after writing, there is no advance warning before the file write occurs.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

The script reads HUBSPOT_ACCESS_TOKEN from the environment to authenticate against HubSpot, but there is no comment, prompt, or explicit user-facing warning near that access. For a code file, accessing credentials is a safety-relevant operation that should be disclosed unless clearly warned elsewhere in the skill materials.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.