Back to skill

Security audit

Attio Enhanced CRM

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent Attio CRM integration, but it has bulk CRM write authority and can expose sensitive CRM error details in logs despite claiming sensitive data is not logged.

Review before installing. Use a least-privileged Attio API key, test against a non-production workspace first, avoid running the batch import example with real contacts until you understand the upload behavior, and treat application logs and exception output as potentially containing CRM data.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
lib/attio_enhanced.py:105
Finding
Verbatim API Error Responses May Expose Sensitive CRM Data in Logs## Vulnerability Details **File Location**: `lib/attio_enhanced.py:105-117` and `lib/attio_enhanced.py:172-185` **Vulnerability Type**: Sensitive information exposure through logging **Risk Level**: Medium **Vulnerable code — synchronous request path:** ```python if response.status_code >= 400: error_text = response.text self.logger.error(f"Error response: {error_text}") # Create enhanced HTTPError with error body error = requests.exceptions.HTTPError( f"{response.status_code} {response.reason} | {error_text}", response=response ) error.error_body = error_text raise error ``` **Vulnerable code — asynchronous request path:** ```python if response.status >= 400: self.logger.error(f"Error response: {response_text}") # Create custom error with full details in the message error = aiohttp.ClientResponseError( request_info=response.request_info, history=response.history, status=response.status, message=f"{response.reason} | {response_text}" ) # Store error text as attribute for easier checking error.error_body = response_text raise error ``` ### Technical Analysis Both request implementations write the complete Attio API response body to the application logger whenever an HTTP error occurs. The same unredacted body is also inserted into exception messages and retained in the custom `error_body` attribute. Error responses from a CRM API may include rejected field values, email addresses, names, company information, record identifiers, validation context, or other business data. Logging these responses verbatim can copy sensitive information into console logs, CI output, centralized monitoring platforms, or long-retention log archives. This behavior also conflicts with the assertion in `README.md` that sensitive data is not logged. Although the authorization header itself is not ...[truncated 1443 chars]
Remediation
## Remediation Suggestions 1. Do not log complete response bodies by default. Log only the HTTP status, request correlation ID, and a sanitized API error code. 2. Remove `error_text` from exception messages. Expose detailed response content only through an explicitly enabled diagnostic mode. 3. Implement a centralized redaction function that removes authorization values, email addresses, CRM field values, tokens, and sensitive identifiers before any response is logged. 4. If `error_body` must remain available programmatically, document it as sensitive and ensure upstream handlers do not serialize or log it automatically. 5. Limit diagnostic response size to prevent excessive or attacker-controlled log content. 6. Configure production logs with least-privilege access, encryption, retention limits, and audit controls. 7. Add tests confirming that representative personal data and token-like values never appear in log output or exception strings. 8. Correct the security documentation so that its logging claims accurately reflect the implementation.

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding
Unbounded and Unverified Python Dependencies Create Supply-Chain Exposure## Vulnerability Details **File Location**: `requirements.txt:1-3` **Vulnerability Type**: Unpinned dependencies without integrity verification **Risk Level**: Low **Vulnerable configuration:** ```text requests>=2.28.0 aiohttp>=3.8.0 tenacity>=8.0.0 ``` ### Technical Analysis All dependencies use open-ended minimum-version constraints. A future installation can therefore resolve to package versions that were not reviewed or tested with this project. The requirements file also provides no cryptographic hashes to verify downloaded artifacts. The named packages are established packages, and the audit found no evidence that the currently declared package names are malicious or typosquatted. The risk arises from allowing uncontrolled future versions and relying entirely on the selected package index and transport configuration. Python packages can execute code during installation or when imported. Consequently, compromise of an upstream release, package index, build artifact, or dependency-resolution environment could introduce code outside the audited project. ### Attack Path 1. A user follows the documented command `pip install -r requirements.txt`. 2. The package resolver selects the newest available versions satisfying the open-ended constraints. 3. A selected release is compromised, malicious, replaced in an untrusted index, or otherwise differs from the reviewed dependency set. 4. Because no exact version or artifact hash is required, installation proceeds without detecting the unexpected artifact. 5. Package-controlled code executes during installation or later when the application imports the dependency. ### Impact Assessment Successful supply-chain compromise could execute code with the privileges of the user or service performing installation or running the Skill. In that scenario, accessible assets could include the `ATTIO_API_KEY`, the Attio workspace context, CRM data processed by the client, project fi ...[truncated 255 chars]
Remediation
## Remediation Suggestions 1. Replace open-ended constraints with exact versions that have been reviewed and tested. 2. Generate and maintain a lock file through a controlled dependency-management workflow. 3. Record cryptographic hashes and install with hash enforcement, such as `pip install --require-hashes`. 4. Use only trusted package indexes and explicitly configure the approved index in CI and deployment environments. 5. Run automated vulnerability and provenance checks whenever dependencies are updated. 6. Review transitive dependencies as well as the three direct dependencies. 7. Apply updates through controlled pull requests with test results and dependency diffs rather than resolving unrestricted versions during production deployment.
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
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (12)

Credential Access

High
Category
Privilege Escalation
Content
export ATTIO_WORKSPACE_ID=your_workspace_id
```

Get API key from: https://app.attio.com/settings/api

Find workspace ID in your Attio URL: `app.attio.com/[workspace-id]/...`
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The README explicitly advertises contact and company data enrichment from external sources but does not disclose that user-provided CRM data may be transmitted to third-party services. In a CRM context, this can expose personally identifiable and commercial data without informed consent, creating privacy, compliance, and data-governance risk even if the implementation is otherwise legitimate.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This code sends contact records containing names, email addresses, company names, and job titles to an external API via `batch_import_contacts`. Although the script is an example, it does not include any confirmation prompt or explicit disclosure that personal data will be transmitted off-system.

External Transmission

Medium
Category
Data Exfiltration
Content
self.workspace_id = workspace_id or os.getenv('ATTIO_WORKSPACE_ID')
        # Attio API base - objects don't use /workspaces/{id} prefix
        # Use /v2/objects/{slug}/records directly
        self.base_url = "https://api.attio.com/v2"
        self.workspace_id = workspace_id or os.getenv('ATTIO_WORKSPACE_ID')
        
        if not self.api_key:
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
94% confidence
Finding
This function enriches contact records and then sends them to the Attio API via batch creation, which can affect user data and transmit personal information externally. Although the code has technical docstrings, it provides no user-facing disclosure, confirmation prompt, or explicit warning about uploading contacts or enrichment-related handling before performing the operation.

Missing User Warnings

Low
Confidence
91% confidence
Finding
The client logs full remote error bodies on HTTP failures, and those bodies may contain CRM records, identifiers, validation details, or other sensitive user data returned by the API. If logs are centrally aggregated or accessible to operators, this creates a secondary data exposure channel without need-to-know controls.

Intent-Code Divergence

Low
Confidence
95% confidence
Finding
The function name and docstring state that it will 'validate and clean' data, yet its implementation only calls validate_crm_data and appends original records to valid_records or invalid_records. No normalization, sanitization, or field cleanup is performed anywhere in the function body, so the documentation overstates what the code does.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.28.0
aiohttp>=3.8.0
tenacity>=8.0.0
Confidence
96% confidence
Finding
The dependency specifier for requests is unpinned, allowing future installs to resolve to different versions over time. This increases supply-chain and reproducibility risk because a newly released or compromised version could be pulled in without review, and known-vulnerable versions are not explicitly excluded.

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
91% confidence
Finding
requests has multiple published advisories, but because the manifest does not pin a version, it is impossible to determine whether installation will select a fixed or affected release. In practice, this uncertainty can expose deployments to known security issues such as credential leakage or TLS/request-handling flaws if an unsafe version is resolved.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.28.0
aiohttp>=3.8.0
tenacity>=8.0.0
Confidence
96% confidence
Finding
The aiohttp dependency is specified with a minimum version only, so installations are not deterministic and may pick up different versions depending on when and where the skill is built. That creates avoidable supply-chain exposure and makes it hard to verify whether deployed environments contain a safe version.

Unverifiable Dependency: aiohttp has 16 known advisory(ies) (CVE-2024-52303 (aiohttp has a memory leak when middleware is enabled when requesting a resource ); CVE-2026-54279 (aiohttp: Host-Only Cookies Become Domain Cookies After CookieJar Persistence); CVE-2026-34514 (AIOHTTP has CRLF injection through multipart part content type header constructi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
91% confidence
Finding
aiohttp also has multiple known advisories, and the lack of version pinning means the deployed version cannot be verified from this manifest alone. That leaves open the possibility of pulling in a release affected by request parsing, cookie, memory, or header-handling vulnerabilities depending on the execution environment.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.28.0
aiohttp>=3.8.0
tenacity>=8.0.0
Confidence
94% confidence
Finding
tenacity is also unpinned, which weakens build reproducibility and allows silent dependency drift. Even if no specific advisory is cited here, unreviewed upstream changes can introduce security or reliability regressions into the skill.