Back to skill

Security audit

Omnium Hub CRM

Security checks for vulnerabilities and agentic risk

Overview

This CRM skill is not proven malicious, but it needs review because it asks for a live API key and can send or change CRM data through an under-disclosed external service.

Install only after confirming that LeadConnectorHQ is the intended Omnium Hub API provider, using a narrowly scoped and revocable API key, and accepting that contact and opportunity data may be transmitted and printed in logs. Prefer a version that reads secrets from protected storage or environment variables and documents the exact supported actions and external hosts.

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

Error
Location
scripts/omnium_client.py:107
Finding
Bearer API Key Exposed Through Command-Line Arguments<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:20-33`; `scripts/omnium_client.py:107-110` **Vulnerability Type**: Credential exposure through process arguments and command history **Risk Level**: High ### Vulnerable Code ```markdown **Usage:** ```bash python3 scripts/omnium_client.py --api-key "YOUR_KEY" contacts --action [lookup|create|update] --email "user@example.com" [other options] ``` **Examples:** * "Find the contact for john@example.com in Omnium Hub." -> `python3 scripts/omnium_client.py --api-key "..." contacts --action lookup --email "john@example.com"` * "Add Jane Doe (jane@test.com) to Omnium Hub." -> `python3 scripts/omnium_client.py --api-key "..." contacts --action create --first-name "Jane" --last-name "Doe" --email "jane@test.com"` ``` ```python def main(): parser = argparse.ArgumentParser(description="Omnium Hub CRM Client") parser.add_argument("--api-key", required=True, help="Omnium Hub API Key (Bearer Token)") ``` ### Technical Analysis The Skill explicitly instructs the agent or user to pass a bearer API key as a command-line argument. Command-line secrets can be exposed through: - Shell history and terminal transcripts. - Agent command logs, execution telemetry, and audit records. - Process listings or process inspection available to other local users while the client is running. - Error reports or debugging output that records the complete invocation. - Automation systems that retain generated commands. Bearer tokens normally grant access without further proof of identity. Anyone obtaining the token can use it independently until it expires or is revoked. Passing the token to the CRM API is necessary for the declared functionality, but exposing it through the process argument vector is not necessary and violates least-exposure principles. ### Attack Path 1. A user provides an Omnium Hub API key to the agent as instructed. 2. The agent constructs a command containing the plaintext key in th ...[truncated 1259 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the required `--api-key` command-line option. 2. Read the token from a protected environment variable or secret manager, for example: ```python import os api_key = os.environ.get("OMNIUM_API_KEY") if not api_key: parser.error("OMNIUM_API_KEY must be provided through a protected secret source") ``` 3. For interactive use, support a non-echoing prompt with `getpass.getpass()` rather than placing the secret in command history. 4. Update `SKILL.md` so examples never interpolate real credentials into command text. 5. Configure agent and CI execution environments to inject the credential through their native secret facilities. 6. Redact authorization values from logs, traces, exceptions, and telemetry. 7. Assign the API key only the CRM scopes needed for the requested operation and use short-lived credentials where supported. 8. Rotate any key that may already have appeared in command history or execution logs. ]]>

other

Warning
Location
scripts/omnium_client.py:6
Finding
Undisclosed Transmission of Credentials and Customer PII to a Differently Branded External Service<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:6-11`; `scripts/omnium_client.py:6-16, 29-69, 73-103` **Vulnerability Type**: Undisclosed external transmission of sensitive information **Risk Level**: Medium ### Vulnerable Code The documentation identifies the integration only as Omnium Hub: ```markdown # Omnium Hub Skill This skill allows you to interact with the **Omnium Hub** CRM. ## Prerequisites To use this skill, you need an **Omnium Hub API Key**. - If you do not have an API key, ask the user for it: "Please provide your Omnium Hub API Key to proceed." - Once provided, use it in the scripts below. ``` The implementation sends the credential and CRM information to a different branded domain: ```python # Constants - Update these if needed BASE_URL = "https://services.leadconnectorhq.com" VERSION = "2021-07-28" # Default V2 API version def get_headers(api_key): return { "Authorization": f"Bearer {api_key}", "Version": VERSION, "Content-Type": "application/json", "Accept": "application/json" } ``` Contact data is transmitted in URL query parameters or JSON request bodies: ```python def manage_contacts(args, headers): url = f"{BASE_URL}/contacts" if args.action == "lookup": if not args.email and not args.phone: print("Error: lookup requires --email or --phone") sys.exit(1) params = {} if args.email: params['query'] = args.email elif args.phone: params['query'] = args.phone # Use search endpoint for lookup resp = requests.get(f"{url}/search", headers=headers, params=params) data = handle_response(resp) print(json.dumps(data, indent=2)) elif args.action == "create": payload = { "firstName": args.first_name, "lastName": args.last_name, "email": args.email, "phone": args.phone } # Remove None values ...[truncated 5035 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Clearly identify `services.leadconnectorhq.com` in `SKILL.md` before requesting any API key or customer data. 2. Document the relationship between Omnium Hub and LeadConnector, including whether LeadConnector is the official API provider or an authorized data processor. 3. Describe which data fields are transmitted, why they are required, and the applicable retention and privacy policies. 4. Require explicit user approval before the first transmission when the external recipient is not evident from the requested task. 5. Keep a strict allowlist of approved HTTPS hosts and reject redirects to unapproved domains. 6. Where the API supports it, avoid placing PII in query strings; use a request body for sensitive search parameters. 7. Add explicit request timeouts and conservative redirect handling, for example: ```python resp = requests.get( f"{url}/search", headers=headers, params=params, timeout=(5, 30), allow_redirects=False, ) ``` 8. Replace raw response and error printing with structured, redacted output. Do not log authorization headers, complete customer records, or unnecessary response bodies. 9. Minimize payload fields to those needed for each requested action. 10. Use narrowly scoped, short-lived API credentials and provide clear revocation guidance. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (9)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill description promises broad CRM and appointment management behavior, while the visible instructions only cover contacts and opportunities and the analysis indicates use of LeadConnectorHQ endpoints rather than clearly scoped Omnium Hub appointment resources. This mismatch can mislead operators about what the skill really does, reducing informed consent and making unauthorized or unexpected external actions harder to detect.

Missing User Warnings

High
Confidence
96% confidence
Finding
The skill explicitly asks the user to provide an API key and instructs that it be used in scripts, but gives no safety guidance on secret handling. This encourages collection of sensitive credentials in conversation and likely exposure via command-line arguments, logs, shell history, or downstream tooling, which is especially dangerous because the key grants live CRM access.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill invokes a Python client that performs networked CRM operations, but the skill manifest does not declare any tool scope or allowed-tools boundaries. This weakens enforcement and review because the agent may use capabilities broader than what is explicitly disclosed, increasing the chance of unintended external data access.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The phrase 'Use for all CRM-related tasks' gives the skill an overly broad activation scope, which can cause it to be selected for requests far beyond the implemented and reviewed functionality. Overbroad routing increases the risk of unnecessary access to customer data and of the skill being used in contexts where its limitations and side effects are not understood.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The code transmits contact PII such as first name, last name, email, and phone number via HTTP POST and PUT requests to an external service. While errors are printed, there is no confirmation prompt, user-facing disclosure, or explanatory comment/docstring warning that user data will be sent off-system.

External Transmission

Medium
Category
Data Exfiltration
Content
# Remove None values
        payload = {k: v for k, v in payload.items() if v}
        
        resp = requests.post(url, headers=headers, json=payload)
        data = handle_response(resp)
        print(json.dumps(data, indent=2))
Confidence
80% 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
# Remove None values
        payload = {k: v for k, v in payload.items() if v}
        
        resp = requests.post(url, headers=headers, json=payload)
        data = handle_response(resp)
        print(json.dumps(data, indent=2))
Confidence
80% 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
}
        payload = {k: v for k, v in payload.items() if v}

        resp = requests.put(f"{url}/{args.contact_id}", headers=headers, json=payload)
        data = handle_response(resp)
        print(json.dumps(data, indent=2))
Confidence
80% 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
84% confidence
Finding
The create opportunity path issues a POST request containing pipeline, stage, title, and contact linkage data to a remote service. The script does not provide a confirmation, visible disclosure, or documentation comment warning the user that CRM data will be transmitted externally.

Static analysis

No suspicious patterns detected.