Back to skill

Security audit

Pangolinfo Scrape (Legacy)

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it says, but it stores a long-lived Pangolin access token in a predictable local file using unsafe handling that users should review before installing.

Install only if you are comfortable sending searches, Amazon URLs, screenshot requests, follow-up prompts, and Pangolin credentials or tokens to Pangolin. Prefer PANGOLIN_TOKEN over email/password, protect or remove ~/.pangolin_token after use, and treat that token as account access because the documentation says it is permanent unless the account is deactivated.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/pangolinfo.py:35
Finding

Permanent Bearer Token Stored Using a Non-Atomic, Symlink-Following File Operation

Content
View full analysis

Vulnerability Details

File Location: scripts/pangolinfo.py, lines 35 and 63-66
Vulnerability Type: Insecure secret storage and unsafe file creation
Risk Level: Medium

Vulnerable Code

python
TOKEN_CACHE_PATH = Path.home() / ".pangolin_token"

def save_cached_token(token):
    """Save token to cache file."""
    TOKEN_CACHE_PATH.write_text(token)
    TOKEN_CACHE_PATH.chmod(0o600)

The associated documentation states that the cached tokens are permanent unless the account is deactivated:

markdown
- Tokens are **permanent** -- they do not expire on their own
- A token becomes invalid only if the account is deactivated
- The script caches tokens at `~/.pangolin_token`

Technical Analysis

The token is first written through Path.write_text() and only afterward restricted to mode 0600. When the cache file is initially created, its permissions are determined by the process umask. This creates a time-of-check/time-of-protection window during which the token may be readable by other local users. If the process terminates between the write and chmod(), the file may remain with overly permissive permissions.

The fixed cache path is also used without checking whether it is a symbolic link. Both write_text() and chmod() follow symbolic links. An attacker who can pre-create or replace ~/.pangolin_token with a symbolic link could therefore redirect the token write to another user-writable target and cause the target's permissions to be changed to 0600.

Because the cached value is a bearer token, possession is sufficient for authentication. The long-lived token design increases the useful exploitation window.

Attack Path

  1. An attacker with local access and the ability to manipulate the token-cache path monitors or pre-creates ~/.pangolin_token.
  2. The attacker either waits for creation under a permissive umask or replaces the path with a symbolic link to a suitable target.
  3. The victim invokes the Skill without ...[truncated 1244 chars]
Remediation
View remediation

Remediation Suggestions

  1. Create secret files with restrictive permissions from the outset rather than writing first and applying chmod() afterward.
  2. Reject symbolic links by using O_NOFOLLOW where supported.
  3. Write the token to a securely created temporary file in the same directory, flush and synchronize it, and atomically replace the cache file.
  4. Verify that existing cache files are regular files, owned by the current user, and have mode 0600 before reading them.
  5. Prefer an operating-system credential store instead of a plaintext file where practical.
  6. Support token rotation and revocation because the documentation identifies tokens as permanent.

An example hardened approach is:

python
import os
import stat
import tempfile

def save_cached_token(token):
    cache_dir = TOKEN_CACHE_PATH.parent

    fd, temp_path = tempfile.mkstemp(
        prefix=".pangolin_token.",
        dir=str(cache_dir),
        text=True,
    )

    try:
        os.fchmod(fd, 0o600)

        with os.fdopen(fd, "w") as cache:
            cache.write(token)
            cache.flush()
            os.fsync(cache.fileno())

        if TOKEN_CACHE_PATH.is_symlink():
            raise RuntimeError("Refusing to replace a symbolic-link token cache")

        os.replace(temp_path, TOKEN_CACHE_PATH)
    except Exception:
        try:
            os.close(fd)
        except OSError:
            pass
        try:
            os.unlink(temp_path)
        except OSError:
            pass
        raise

The cache-loading path should independently use lstat() or an equivalent no-follow operation to ensure the cache is a regular file owned by the current user with no group or other access permissions.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (8)

Tainted flow: 'req' from os.environ.get (line 190, credential/environment) → urllib.request.urlopen (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/pangolinfo.py (reported line 79)May include surrounding context.

python
method="POST",
    )
    try:
        with urllib.request.urlopen(req, timeout=30) as resp:
            result = json.loads(resp.read())
    except urllib.error.URLError as e:
        print(json.dumps({"error": f"Network error during auth: {e}"}), file=sys.stderr)

Tainted flow: 'req' from os.environ.get (line 190, credential/environment) → urllib.request.urlopen (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/pangolinfo.py (reported line 196)May include surrounding context.

python
headers=headers,
                method="POST",
            )
            with urllib.request.urlopen(req, timeout=120) as resp:
                return json.loads(resp.read())
        except urllib.error.HTTPError as e:
            error_body = e.read().decode() if e.fp else ""

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill advertises functionality that necessarily uses sensitive capabilities—environment variables for credentials, local token cache access, shell/Python execution, file read/write, and outbound network calls—but does not declare any explicit tool scope or permission boundaries. This makes the runtime privileges opaque to users and reviewers, increasing the risk of over-broad execution, credential exposure, or unintended local file access if the implementation is invoked in a permissive agent environment.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill encourages users to submit search queries, URLs, follow-up prompts, screenshots, and Amazon scraping parameters to a third-party Pangolin API, but it does not clearly warn that this data leaves the local environment. In agent settings, users may unknowingly send sensitive research topics, internal URLs, or location/account-context parameters to an external service, creating privacy, compliance, and data handling risks.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The documentation describes sending Google search URLs and optional screenshots to Pangolin's third-party scraping endpoint but does not warn users that their queries and captured page content may be transmitted to and retained by an external service. This can lead to unintended disclosure of sensitive search terms, internal investigation topics, or screenshot contents, especially when agents pass through user-supplied queries automatically.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

This markdown file documents that tokens are cached at ~/.pangolin_token and that fresh login uses PANGOLIN_EMAIL and PANGOLIN_PASSWORD, including a plaintext auth request body example. Because the file describes behaviors affecting credentials and local secret storage, it should include an explicit warning about protecting these values and the cached token file.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The script transmits account email and password to an external service for login without an explicit runtime warning or consent step. In an agent-skill context, users or operators may not realize raw credentials are being sent off-box, increasing the risk of unintended secret disclosure to a third party.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The client forwards user search queries, follow-up prompts, Amazon URLs, and related parameters to Pangolin's external scraping API without an explicit privacy notice at execution time. In an agent environment, this can expose potentially sensitive research terms, target URLs, or business context to a third-party processor without sufficient transparency.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.