Back to skill

Security audit

Apify Keys

Security checks for vulnerabilities and agentic risk

Overview

This skill handles real API keys and database access, but it embeds database credentials, can bulk-print secrets, and mutates the host Python environment at runtime.

Do not install this skill in a general agent environment unless you own the ColdCore database and Apify accounts and have reviewed the operational risk. The database password and any exposed Apify keys should be rotated, runtime pip installation should be removed, list output should redact secrets, and access should be restricted to authorized callers only.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/get_key.py:39
Finding

Hard-Coded Production Database Credentials

Content
View full analysis

Vulnerability Details

File Location: scripts/get_key.py, lines 39-48
Vulnerability Type: Hard-coded credentials and insecure secret management
Risk Level: Critical

Vulnerable Code

python
def get_db_connection():
    """Connect to ColdCore MySQL database."""
    return mysql.connector.connect(
        host=os.environ.get("COLDCORE_HOST", "208.87.131.78"),
        user=os.environ.get("COLDCORE_USER", "avnadmin"),
        password=os.environ.get("COLDCORE_PASS", "lRHS2T8XagsA5z4u3ZYd"),
        database=os.environ.get("COLDCORE_DB", "lead_generator"),
        connect_timeout=10
    )

Technical Analysis

The script embeds a public database IP address, database username, plaintext password, and database name as fallback values. Consequently, environment variables are optional rather than mandatory, and anyone who obtains the Skill package also obtains credentials that can be used to attempt direct access to the remote ColdCore MySQL service.

Storing credentials in source code violates secure secret-management principles. Source files can be copied, published, cached, logged, included in backups, or retained in version-control history. The username avnadmin also appears administrative, potentially granting substantially more access than the narrow read-and-update operations required by the key-rotation function.

This access exceeds minimum privilege if the account can read or modify tables other than the specific fields needed in scrape_sm_accounts. The audit could not verify the server-side grants, network allowlist, or TLS configuration, so the precise database privileges remain unknown.

Attack Path

  1. An attacker obtains the Skill source through distribution, repository access, a package archive, or local filesystem access.
  2. The attacker extracts the default host, username, password, and database name from get_db_connection().
  3. The attacker attempts a direct MySQL con ...[truncated 1119 chars]
Remediation
View remediation

Remediation Suggestions

  1. Immediately revoke and rotate the exposed database password. Treat it as compromised even if the project was not intentionally published.
  2. Remove all secret fallback values from source code. Fail closed when required variables are absent:
    python
    required = ("COLDCORE_HOST", "COLDCORE_USER", "COLDCORE_PASS", "COLDCORE_DB")
    missing = [name for name in required if not os.environ.get(name)]
    if missing:
        raise RuntimeError(f"Missing required configuration: {', '.join(missing)}")
    
  3. Supply credentials through an approved secret manager or workload identity mechanism rather than command-line arguments, source files, or broadly readable environment files.
  4. Create a dedicated database identity for this Skill. Restrict it to the required columns and operations on scrape_sm_accounts; do not use an administrative account.
  5. Restrict database network access to approved workloads using firewall rules, private networking, or an authenticated proxy.
  6. Require certificate-validated TLS for the MySQL connection so credentials and returned API keys cannot traverse the network in plaintext.
  7. Review database access logs for use of the exposed account and inspect source-control history, build artifacts, logs, and backups for further copies of the secret.
  8. Add secret scanning to commits and release pipelines to prevent recurrence.

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/get_key.py:88
Finding

Unauthenticated Retrieval and Bulk Disclosure of Live API Keys

Content
View full analysis

Vulnerability Details

File Location: scripts/get_key.py, lines 51-72, 88-102, and 130-176
Vulnerability Type: Excessive secret access and missing caller authorization
Risk Level: High

Vulnerable Code

python
def get_next_key(conn):
    """Get the least-recently-used active Apify account."""
    cursor = conn.cursor(dictionary=True)

    # Select least recently used active account with balance
    cursor.execute("""
        SELECT id, api_key, email, label, balance, last_used
        FROM scrape_sm_accounts
        WHERE channel = 'apify'
          AND active = 1
          AND api_key IS NOT NULL
          AND (balance IS NULL OR balance > 0)
        ORDER BY last_used ASC
        LIMIT 1
    """)
    row = cursor.fetchone()
python
def list_all_keys(conn):
    """List all active Apify accounts with balances."""
    cursor = conn.cursor(dictionary=True)
    cursor.execute("""
        SELECT id, api_key, email, label, balance, last_used
        FROM scrape_sm_accounts
        WHERE channel = 'apify'
          AND active = 1
          AND api_key IS NOT NULL
        ORDER BY last_used ASC
    """)
    rows = cursor.fetchall()
    cursor.close()
    return rows
python
if args.list:
    rows = list_all_keys(conn)
    if args.json:
        print(json.dumps(rows, indent=2, default=str))
    else:
        print(f"{'ID':>4}  {'Email':<40}  {'Balance':>8}  {'Last Used':<20}  Key Prefix")
        print("-" * 110)
        for r in rows:
            last = time.strftime("%Y-%m-%d %H:%M", time.localtime(r["last_used"])) if r["last_used"] else "never"
            key_prefix = r["api_key"][:20] + "..." if r["api_key"] else "N/A"
            print(f"{r['id']:>4}  {r['email']:<40}  {r['balance'] or 0:>8.3f}  {last:<20}  {key_prefix}")
        print(f"\nTotal: {len(rows)} active accounts")
    return
python
if args.json:
    
...[truncated 3074 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove the ability to list or serialize complete API keys. In particular, eliminate --list --json or ensure its output contains only non-sensitive identifiers and redacted metadata.
  2. Authenticate and authorize every caller. Apply explicit policy defining which workload may request which credential and for what purpose.
  3. Replace direct disclosure of long-lived keys with a credential broker that performs Apify operations on behalf of callers or issues short-lived, narrowly scoped tokens where supported.
  4. Restrict database selection to the minimum required columns. Do not retrieve email or label unless essential to the operation.
  5. Separate administrative inventory functionality from routine key rotation and protect it with stronger authorization.
  6. Avoid passing credentials through shell variables or logs where possible. Return secrets through an authenticated IPC channel or process-local secret mechanism and ensure output is never recorded.
  7. Add retrieval rate limits, caller-specific audit records, anomaly detection, and alerts for bulk or repeated key access.
  8. Rotate all Apify keys that may have been exposed through this interface and review Apify usage logs for unauthorized activity.
  9. Apply server-side token scopes and account-level spending limits to reduce damage if a key is disclosed.
  10. Make key selection and timestamp updates transactional to avoid concurrent callers receiving the same credential.

T08 · Insecure Dependencies

Error
Location
scripts/get_key.py:14
Finding

Runtime Installation of Unpinned Dependencies into the Host Environment

Content
View full analysis

Vulnerability Details

File Location: scripts/get_key.py, lines 14-34
Vulnerability Type: Unsafe runtime dependency installation and supply-chain exposure
Risk Level: High

Vulnerable Code

python
try:
    import mysql.connector
except ImportError:
    print("Installing mysql-connector-python...", file=sys.stderr)
    import subprocess
    subprocess.check_call([
        sys.executable, "-m", "pip", "install",
        "--break-system-packages", "-q", "mysql-connector-python"
    ])
    import mysql.connector

try:
    import requests
except ImportError:
    print("Installing requests...", file=sys.stderr)
    import subprocess
    subprocess.check_call([
        sys.executable, "-m", "pip", "install",
        "--break-system-packages", "-q", "requests"
    ])
    import requests

Technical Analysis

When either dependency is missing, the Skill automatically invokes pip during normal execution. Neither package is pinned to a reviewed version, and no integrity hash is supplied. The effective dependency selected at execution time can therefore change independently of the audited Skill.

Python package installation can execute package build or installation logic. If the configured package index, DNS path, proxy, certificate trust store, package account, or upstream release is compromised, running the Skill can introduce and execute attacker-controlled code.

The --break-system-packages option deliberately bypasses protections intended to prevent pip from modifying a system-managed Python installation. This broadens the effect beyond the Skill's own environment and can overwrite or conflict with dependencies used by unrelated applications. Installing packages is not required for the Skill's runtime business function and exceeds minimum operational privilege.

No evidence shows that the named packages are currently malicious. The vulnerability is the unpinned, mutable, autom ...[truncated 1646 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove all runtime pip install behavior from the executable script.
  2. Declare dependencies in a reviewed project manifest and lockfile with exact versions and cryptographic hashes.
  3. Install dependencies during a separate deployment or build phase, not while handling credentials.
  4. Use an isolated virtual environment or immutable container rather than modifying the system Python installation.
  5. Remove --break-system-packages; fail with a clear dependency error if the environment is incomplete.
  6. Configure an approved package repository, enforce TLS validation, and restrict unexpected indexes or dependency links.
  7. Scan locked dependencies for known vulnerabilities and update them through a controlled review process.
  8. Run the Skill as a dedicated, unprivileged operating-system identity with access only to required network destinations and secrets.
  9. Produce a software bill of materials and verify package integrity during deployment.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (15)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared purpose suggests simple key rotation, but the documented behavior also includes listing all keys with metadata, performing live balance checks, and accessing a backend database. That mismatch is dangerous because consumers may trust the skill for a narrow secret-fetch use case while it actually enables broader credential enumeration and external interactions.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill installs Python packages at runtime via pip subprocess calls, which is unnecessary for a key manager and materially expands its power over the host system. In context, this is especially dangerous because the skill already handles sensitive credentials and remote connections, so host compromise or package-chain abuse could expose multiple secrets.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill exposes capabilities that materially exceed what is declared: it relies on shell execution, environment-sourced secrets, database access, and likely network access, yet declares no tool scope or permissions. This creates hidden attack surface and makes it easier for downstream agents or users to invoke sensitive operations without informed consent or sandboxing assumptions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill explicitly prints raw Apify API keys and associated account metadata to stdout for easy piping, but the description lacks a strong warning that highly sensitive credentials will be exposed in command output, logs, and subprocess captures. In agent environments, stdout is often persisted, forwarded, or inspected by other components, increasing the chance of credential leakage and account compromise.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The script modifies the Python environment by installing mysql-connector-python with only a generic stderr message and no explicit user confirmation. Hidden environment mutation is risky in agentic contexts because operators may not realize the skill is performing package installation and potentially changing system state.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
97% confidence
Finding

The script performs implicit runtime package installation via pip using subprocess, which modifies the host environment and executes package-management code without prior approval. In a skill whose purpose is only key rotation, this broadens behavior significantly and creates supply-chain and environment-tampering risk if package sources or execution context are compromised.

Content

Scanner excerpt · scripts/get_key.py (reported line 21)May include surrounding context.

python
except ImportError:
    print("Installing mysql-connector-python...", file=sys.stderr)
    import subprocess
    subprocess.check_call([
        sys.executable, "-m", "pip", "install",
        "--break-system-packages", "-q", "mysql-connector-python"
    ])

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This repeats the same silent-environment-modification issue for the requests package. The concern is amplified by the use of --break-system-packages, which can interfere with system-managed Python environments and cause unintended side effects.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
97% confidence
Finding

This is a second instance of the same unsafe pattern: silently invoking pip through subprocess to install requests at runtime. It gives the skill unnecessary capability to alter the system and introduces dependency-fetching and code-execution risk unrelated to simply returning a database-managed key.

Content

Scanner excerpt · scripts/get_key.py (reported line 32)May include surrounding context.

python
except ImportError:
    print("Installing requests...", file=sys.stderr)
    import subprocess
    subprocess.check_call([
        sys.executable, "-m", "pip", "install",
        "--break-system-packages", "-q", "requests"
    ])

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

The script contains hardcoded default database host, username, password, and database name, then automatically opens a remote connection. Embedded fallback credentials are a severe secret-management flaw: if environment variables are absent, the skill still attempts privileged access using exposed credentials, enabling unauthorized database access and secret leakage.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The live balance-check feature sends retrieved API keys to Apify and performs outbound network activity beyond the manifest's described database-based key rotation. While plausibly operational, it expands the skill's authority and data flow, increasing risk if the skill is used in minimally trusted environments.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The skill transmits bearer tokens to an external API without clear disclosure to the caller, which may violate least-surprise expectations for a simple key-rotation utility. Although sending the token to Apify is functionally necessary for balance checking, this network exfiltration path becomes more sensitive in an agent skill handling secrets.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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/get_key.py (reported line 89)May include surrounding context.

python
def check_balance(api_key):
    """Check Apify account balance via API."""
    resp = requests.get(
        "https://api.apify.com/v2/users/me/usage/monthly",
        headers={"Authorization": f"Bearer {api_key}"},
        timeout=15
    )

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill can list all active Apify accounts, including email, balance, and API key prefixes, which exceeds the stated purpose of returning the next fresh key. This increases secret exposure and enables reconnaissance over the full key inventory if an unauthorized caller invokes list mode.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

Allowing an arbitrary user-supplied API key to be sent to the balance-check endpoint turns the skill into a generic credential validation/probing utility, not just a manager for controlled keys. This can be abused to test external secrets and expands the scope far beyond managed database entries.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

Using the skill changes database state by updating the selected account's last_used timestamp, but this side effect is not prominently disclosed. Hidden state mutation can undermine auditability, surprise operators, and be abused to manipulate rotation order or account utilization patterns.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.