Back to skill

Security audit

Didit Aml Screening

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent Didit AML screening skill, but it needs review because it sends highly sensitive identity data to a third party and provides limited user control over retention, monitoring, and output exposure.

Install only if you are prepared to send AML subject data to Didit and have the required consent or legal basis. Avoid running the helper in logged CI or shared agent sessions unless output is redacted, and prefer adding explicit controls for `save_api_request`, continuous monitoring, and full-response output before routine use.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/screen_aml.py:34
Finding
Sensitive AML identity data may be retained by the remote provider without explicit opt-in<![CDATA[ ## Vulnerability Details **File Location**: `scripts/screen_aml.py:34-47` **Related Documentation**: `SKILL.md:86-87` **Vulnerability Type**: Privacy-unsafe remote data retention default **Risk Level**: Medium ### Complete Code Snippet ```python def screen_aml(full_name: str, date_of_birth: str = None, nationality: str = None, document_number: str = None, entity_type: str = "person", threshold: int = None, vendor_data: str = None) -> dict: api_key = get_api_key() payload = {"full_name": full_name, "entity_type": entity_type} if date_of_birth: payload["date_of_birth"] = date_of_birth if nationality: payload["nationality"] = nationality if document_number: payload["document_number"] = document_number if threshold is not None: payload["aml_match_score_threshold"] = threshold if vendor_data: payload["vendor_data"] = vendor_data r = requests.post(ENDPOINT, headers={"x-api-key": api_key, "Content-Type": "application/json"}, json=payload, timeout=60) ``` The documented API behavior is: ```text save_api_request | boolean | No | true | Save in Business Console ``` ### Technical Analysis The script legitimately needs to send the screening subject's identity data to the declared Didit AML endpoint. However, it does not set the API's `save_api_request` field and provides no command-line control for it. According to `SKILL.md`, the service defaults this option to `true`. Consequently, names, dates of birth, nationalities, document numbers, and tracking identifiers may be stored in the provider's Business Console even when the user only intends to conduct a one-time screening. Date-of-birth and government-document information are particularly sensitive personal data. The transmission itself is not covert exfiltration: it uses HTTPS, targets the documented service, and is central to the Skill's declared purpose. ...[truncated 1528 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Set `save_api_request` to `false` in the request payload by default: ```python payload = { "full_name": full_name, "entity_type": entity_type, "save_api_request": False, } ``` 2. Add an explicit opt-in command-line option such as `--save-api-request`. 3. Only change the payload value to `true` when the user supplies that option. 4. Clearly disclose what data is transmitted, whether it will be retained, and how users can request deletion. 5. Avoid collecting optional identity fields unless they are necessary for the required match accuracy. 6. Document the provider's retention period, access controls, deletion procedure, data-processing role, and applicable regional transfer requirements. 7. Consider requiring confirmation before transmitting a document number or other high-impact identifier. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/screen_aml.py:69
Finding
Complete AML response containing sensitive identity and compliance data is printed to standard output<![CDATA[ ## Vulnerability Details **File Location**: `scripts/screen_aml.py:69-72` **Vulnerability Type**: Sensitive information exposure through process output **Risk Level**: Medium ### Complete Code Snippet ```python result = screen_aml(args.name, args.dob, args.nationality, args.doc_number, args.entity_type, args.threshold, args.vendor_data) print(json.dumps(result, indent=2)) aml = result.get("aml", {}) ``` ### Technical Analysis The script serializes and prints the complete remote API response. The response format documented in `SKILL.md` can include: - The screened subject's full name. - Date of birth and nationality. - Document number. - Sanctions, PEP, and adverse-media matches. - Match and risk scores. - Related entities and source details. - Request and tracking identifiers. Standard output is frequently retained outside the immediate terminal. It may be collected by CI systems, agent execution transcripts, shell redirection, centralized logging, job runners, notebooks, or monitoring infrastructure. Printing the full object by default therefore expands access to sensitive identity and AML information beyond the caller and the screening provider. This is a confidentiality weakness rather than code execution or privilege escalation. Exploitation requires access to captured output or the ability to induce an operator to run the command in a logged environment. ### Attack Path 1. A user submits identity data for AML screening. 2. The provider returns screening details and may echo the submitted identity fields under `screened_data`. 3. The script passes the entire result to `json.dumps`. 4. The complete serialized response is written to standard output. 5. A terminal recorder, CI system, agent platform, redirected file, or centralized logging service captures that output. 6. A person with access to the captured output obtains identity and compliance information beyond what is needed for the summary result. ...[truncated 931 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Print a minimal summary by default, such as request status, total hit count, and a non-sensitive request identifier. 2. Add an explicit `--full-output` option for authorized users who require the raw response. 3. Redact sensitive fields before any output, including: - `document_number` - `date_of_birth` - Full identity values under `screened_data` - Unnecessary tracking identifiers 4. Provide a structured redaction function that recursively filters both documented and unexpected sensitive fields. 5. Write sensitive detailed reports only to an explicitly selected destination with restrictive permissions. 6. Warn users that full output may contain regulated personal and compliance data. 7. Ensure CI and agent integrations disable command-output retention or apply equivalent redaction. ]]>

T08 · Insecure Dependencies

Note
Location
SKILL.md:297
Finding
Documentation instructs users to install an unpinned third-party dependency<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:297` **Vulnerability Type**: Unpinned third-party dependency **Risk Level**: Low ### Complete Code Snippet ```bash # Requires: pip install requests ``` ### Technical Analysis The installation instruction requests the latest package version resolved under the name `requests`, without a version constraint, lock file, or integrity hash. Although `requests` is a well-known package and there is no evidence that the project intentionally references a malicious dependency, mutable dependency resolution reduces build reproducibility and makes future installations dependent on the state of the configured package index. The instruction also does not constrain the package source. A misconfigured or attacker-controlled Python package index could provide an unintended artifact under the expected package name. This is a supply-chain hardening weakness. It is not evidence of an existing malicious package or active compromise. ### Attack Path 1. A user follows the documented `pip install requests` instruction. 2. `pip` resolves the package from the user's configured package indexes. 3. The resolver selects whatever compatible release is current at installation time. 4. If the selected upstream artifact, dependency, or configured index is compromised, malicious installation or runtime behavior may execute under the user's privileges. 5. The malicious dependency could access data and permissions available to the Python process, potentially including the `DIDIT_API_KEY` environment variable and AML data processed by the script. The attainable privileges are those of the user running `pip` or the script. Impact is greater if installation is performed globally, as an administrator, or in an environment containing additional credentials. ### Impact Assessment Potential impacts include: - Non-reproducible installations and unexpected compatibility failures. - Exposure to a future compromised release or trans ...[truncated 359 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Declare an audited, compatible dependency version or version range in a requirements file. 2. Generate a lock file containing exact transitive dependency versions. 3. Use integrity hashes, for example through a hash-locked requirements file and `pip install --require-hashes`. 4. Explicitly use the intended trusted package index. 5. Install dependencies in an isolated virtual environment rather than globally or with administrator privileges. 6. Regularly review and update pinned dependencies through a controlled vulnerability-management process. 7. Document a reproducible installation command based on the locked dependency set. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • 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
Findings (10)

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

Critical
Category
Data Flow
Content
payload["aml_match_score_threshold"] = threshold
    if vendor_data:
        payload["vendor_data"] = vendor_data
    r = requests.post(ENDPOINT,
                      headers={"x-api-key": api_key, "Content-Type": "application/json"},
                      json=payload, timeout=60)
    if r.status_code not in (200, 201):
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill declares environment and network capabilities via metadata/examples but does not explicitly constrain them with a tool/permission scope. In an agent setting, that can lead to overbroad execution privileges and makes it harder for hosts or reviewers to enforce least privilege.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The documentation describes sending highly sensitive personal data such as full name, date of birth, nationality, and document number to a third-party AML provider, but it does not prominently warn about privacy, compliance, or data handling implications. Users or agents may disclose regulated PII without informed consent or appropriate legal basis.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
The skill is presented as an AML screening integration, but the documentation also includes account registration, email verification, and billing top-up flows. Expanding instructions beyond the advertised scope increases the chance an agent performs unintended account lifecycle or payment actions, which are higher risk than read-only screening.

External Transmission

Medium
Category
Data Exfiltration
Content
```python
import requests

response = requests.post(
    "https://verification.didit.me/v3/aml/",
    headers={"x-api-key": "YOUR_API_KEY", "Content-Type": "application/json"},
    json={
Confidence
93% confidence
Finding
This example performs an outbound POST request to a third-party AML endpoint and includes sensitive identity fields in the request body. In context, external transmission is expected for the feature, but it is still security-relevant because it exports PII to an external service and depends on correct consent, scoping, and secret handling.

External Transmission

Medium
Category
Data Exfiltration
Content
```python
import requests

response = requests.post(
    "https://verification.didit.me/v3/aml/",
    headers={"x-api-key": "YOUR_API_KEY", "Content-Type": "application/json"},
    json={
Confidence
93% confidence
Finding
This example performs an outbound POST request to a third-party AML endpoint and includes sensitive identity fields in the request body. In context, external transmission is expected for the feature, but it is still security-relevant because it exports PII to an external service and depends on correct consent, scoping, and secret handling.

External Transmission

Medium
Category
Data Exfiltration
Content
```

```typescript
const response = await fetch("https://verification.didit.me/v3/aml/", {
  method: "POST",
  headers: { "x-api-key": "YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({
Confidence
91% confidence
Finding
This TypeScript example also sends AML screening data to a third-party endpoint over the network. While aligned with the skill's purpose, it still creates real exposure of sensitive personal data and normalizes outbound transmission without an adjacent privacy and consent warning.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The continuous monitoring section states that screened sessions are automatically re-screened and may trigger webhook notifications, but it does not clearly warn that processing is ongoing after the initial request. That can create hidden persistence of surveillance and continued third-party data processing beyond the user's immediate expectation.

External Transmission

Medium
Category
Data Exfiltration
Content
payload["aml_match_score_threshold"] = threshold
    if vendor_data:
        payload["vendor_data"] = vendor_data
    r = requests.post(ENDPOINT,
                      headers={"x-api-key": api_key, "Content-Type": "application/json"},
                      json=payload, timeout=60)
    if r.status_code not in (200, 201):
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
93% confidence
Finding
The script sends sensitive personal data such as full name, date of birth, nationality, and document number to a third-party AML screening provider without any explicit consent prompt, warning, or minimization guardrail in the user flow. In a compliance/KYC context this transfer may be expected, but the lack of clear notice and data-handling constraints increases privacy, legal, and misuse risk.

Static analysis

No suspicious patterns detected.