Back to skill

Security audit

Didit Verification Management

Security checks for vulnerabilities and agentic risk

Overview

This Didit admin skill is not malicious, but it needs review because it handles identity-verification records, credentials, billing, and destructive account actions with weak safeguards.

Install only if you are comfortable giving the agent access to a Didit administrative API key and identity-verification data. Avoid running the helper scripts in shared terminals or CI logs, do not paste full API responses into chats, confirm any delete/status/billing/webhook action manually, and prefer scoped keys plus redacted output.

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

Error
Location
scripts/setup_account.py:68
Finding
Authentication secrets are exposed through process arguments and standard output<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup_account.py:68-104` **Vulnerability Type**: Credential exposure through command-line arguments and unredacted output **Risk Level**: High ### Vulnerable Code ```python def main(): parser = argparse.ArgumentParser(description="Didit account setup") sub = parser.add_subparsers(dest="command", required=True) reg_p = sub.add_parser("register", help="Register a new account") reg_p.add_argument("email", help="Email address") reg_p.add_argument("password", help="Password (min 8 chars, 1 upper, 1 lower, 1 digit, 1 special)") ver_p = sub.add_parser("verify", help="Verify email with OTP code") ver_p.add_argument("email", help="Email used during registration") ver_p.add_argument("code", help="6-character code from email") log_p = sub.add_parser("login", help="Login to existing account") log_p.add_argument("email", help="Account email") log_p.add_argument("password", help="Account password") args = parser.parse_args() if args.command == "register": result = register(args.email, args.password) print(json.dumps(result, indent=2)) print(f"\n--- Check {args.email} for your 6-character verification code ---") elif args.command == "verify": result = verify_email(args.email, args.code) print(json.dumps(result, indent=2)) api_key = result.get("application", {}).get("api_key", "") org_uuid = result.get("organization", {}).get("uuid", "") app_uuid = result.get("application", {}).get("uuid", "") print(f"\n--- Account ready! ---") print(f"API Key: {api_key}") print(f"Org UUID: {org_uuid}") print(f"App UUID: {app_uuid}") print(f"\nSet this in your environment:") print(f' export DIDIT_API_KEY="{api_key}"') elif args.command == "login": result = login(args.email, args.password) print(json.dumps(result, indent=2)) prin ...[truncated 2304 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove password and OTP positional arguments. 2. Read passwords interactively with `getpass.getpass()` so they are not echoed or embedded in ordinary command history. 3. Accept secrets from a protected secret manager or restricted file descriptor for non-interactive automation. 4. Do not print complete authentication responses. Construct an explicit allowlist of non-sensitive fields for display. 5. Redact `api_key`, `access_token`, `refresh_token`, passwords, OTPs, and similar fields recursively before logging. 6. Do not print an `export` command containing the API key. Provide a generic instruction such as `export DIDIT_API_KEY="<stored securely>"`. 7. Ensure CI systems mask known secret values and disable command tracing around authentication. 8. Encourage short-lived, narrowly scoped credentials and document immediate revocation procedures for accidental exposure. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/create_session.py:102
Finding
Complete identity-verification decisions are written to standard output without redaction<![CDATA[ ## Vulnerability Details **File Location**: `scripts/create_session.py:102-117` **Vulnerability Type**: Unredacted disclosure of sensitive identity-verification data **Risk Level**: Medium ### Vulnerable Code ```python if args.command == "create": result = create_session(args.workflow_id, args.vendor_data, args.callback, args.language, args.metadata) print(json.dumps(result, indent=2)) print(f"\n--- Session created ---") print(f"Session ID: {result.get('session_id')}") print(f"URL: {result.get('url')}") print(f"Status: {result.get('status')}") elif args.command == "get": result = get_decision(args.session_id) print(json.dumps(result, indent=2)) print(f"\n--- Status: {result.get('status')} ---") elif args.command == "list": result = list_sessions(args.status, args.vendor_data, args.page) print(json.dumps(result, indent=2)) print(f"\n--- {result.get('count', 0)} session(s) total ---") ``` ### Technical Analysis The `get` operation prints the complete decision object, while the `list` and `create` operations also print complete API responses. The Skill documentation states that a decision can contain ID-verification results, liveness checks, face-match results, AML screening, phone and email verification, proof-of-address data, database validation, IP analysis, reviews, and temporary image URLs. Standard output is not an appropriate default destination for this class of data because it is commonly retained by shell logging, CI/CD systems, observability platforms, agent transcripts, and support tooling. The implementation provides no field-level redaction, warning, output-permission control, or explicit opt-in for full records. ### Attack Path 1. An authorized operator retrieves a verification decision with the `get` command. 2. The script serializes the entire API response to standard output. 3. A terminal r ...[truncated 1016 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Print only a minimal summary by default, such as the session ID and status. 2. Require an explicit option such as `--full-output` before returning the complete decision. 3. Display a clear warning and request confirmation before emitting a full identity record interactively. 4. Redact names, addresses, dates of birth, identification numbers, phone numbers, email addresses, biometric results, AML details, tokens, and signed or temporary URLs. 5. If full output is required, support writing it directly to a user-selected file created with restrictive permissions, such as mode `0600`. 6. Avoid sending full records to application logs, CI logs, or agent conversation history. 7. Apply retention limits and access controls to any intentionally stored output. 8. Use structured field allowlists rather than blocklists so newly added API fields are not exposed automatically. ]]>

T08 · Insecure Dependencies

Note
Location
SKILL.md:855
Finding
Dependency installation instructions use an unpinned package version<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:855` **Vulnerability Type**: Unpinned third-party dependency installation **Risk Level**: Low ### Vulnerable Code ```bash pip install requests ``` ### Technical Analysis The installation command does not constrain the `requests` package to a reviewed version and does not verify artifact hashes. Consequently, installations performed at different times can resolve to different package and transitive-dependency versions. The package name is legitimate and the audit found no evidence of deliberate typosquatting or a malicious package. Nevertheless, mutable dependency resolution increases exposure to compromised releases, compromised package indexes, dependency-resolution changes, or future incompatible versions. The command also relies on the user's configured package index without documenting a trusted source or isolated environment. ### Attack Path 1. A user follows the Skill documentation and runs the installation command. 2. `pip` resolves the latest compatible package and transitive dependencies from its configured index. 3. An upstream release, dependency, mirror, or index account is compromised, or an unsafe index configuration takes precedence. 4. The user downloads and installs an artifact that was not part of the reviewed Skill. 5. Malicious package installation or import-time behavior executes with the privileges of the user running the command. This path requires compromise or manipulation of the dependency supply chain; no such compromise was observed in the audited project. ### Impact Assessment A malicious dependency could execute arbitrary Python code with the invoking user's privileges and access files, environment variables, network resources, and credentials available to that process. In this Skill's context, that could include access to `DIDIT_API_KEY`. Because the finding is based on missing version and integrity controls rather than evidence of a currently malicious dep ...[truncated 43 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Declare dependencies in a reviewed requirements or lock file. 2. Pin `requests` and its transitive dependencies to tested versions. 3. Include cryptographic hashes and install with hash verification, for example through `pip install --require-hashes -r requirements.txt`. 4. Review and update dependency pins regularly to incorporate security patches. 5. Install dependencies inside an isolated virtual environment. 6. Document the expected trusted package index and avoid configurations that mix untrusted indexes. 7. Consider automated dependency vulnerability scanning and controlled update review. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (30)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The description substantially overstates the code's capabilities. The supplied code only interacts with session-related endpoints under the Didit verification API: POST /session/ to create sessions, GET /session/{id}/decision/ to retrieve a decision, and GET /sessions/ to list sessions. It does not implement any of the many other declared administrative areas such as accounts, API keys, workflows, questionnaires, users, billing, blocklists, or webhooks. The primary purpose is therefore much narrower than declared, making the description inaccurate.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The code chunk is narrowly scoped to workflow CRUD operations and minor workflow configuration fields such as label, AML, liveness, and face match. The declared description substantially overstates functionality by describing a comprehensive Didit platform administration skill spanning many categories and 45+ endpoints. While workflows are part of the declared description, the supplied code does not implement the vast majority of the claimed capabilities. This is a description-to-behavior mismatch due to materially overstating the skill's actual behavior and primary scope.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The description substantially overstates the code's functionality. The supplied script only supports three authentication/account-bootstrap actions against Didit's auth API: register, verify-email, and login. While account creation is covered, nearly all other declared capabilities—platform management, sessions, workflows, questionnaires, users, billing, blocklists, webhooks, and signature handling—are absent. This is a material description-to-behavior mismatch because the declared primary purpose is a comprehensive platform administration skill, while the actual code is limited to initial account setup/login.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
### 5. Delete Workflow

```
DELETE /v3/workflows/{settings_uuid}/
```

**Response:** `204 No Content`. Existing sessions are not affected.
Confidence
91% confidence
Finding
A workflow delete endpoint is exposed without any documented safety constraints, confirmation pattern, or scoped authorization guidance. In a broad-activation admin skill, an attacker or accidental invocation could remove production workflows and disrupt all future verification operations.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
### 4. Delete Session

```
DELETE /v3/session/{sessionId}/delete/
```

**Response:** `204 No Content`. Permanently deletes all associated data.
Confidence
98% confidence
Finding
The session deletion endpoint permanently removes associated data, yet the skill presents it as a normal operation without strict safeguards. Given that sessions can contain sensitive KYC evidence and decision history, misuse could destroy regulated records, erase fraud evidence, or cause severe compliance and support impact.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
### 5. Delete Questionnaire

```
DELETE /v3/questionnaires/{questionnaire_uuid}/
```

**Response:** `204 No Content`.
Confidence
90% confidence
Finding
Questionnaire deletion is a mutating administrative action that can remove active form configurations relied on by workflows. Without confirmation and access scoping, a prompt injection or mistaken request could break onboarding flows or erase business logic.

Missing User Warnings

High
Confidence
98% confidence
Finding
The verification flow prints the returned API key directly to stdout, which can expose a long-lived secret in terminal scrollback, shell logging, CI logs, screen recordings, or shared sessions. In a platform-administration skill, this is especially dangerous because the API key likely grants broad account access across verification, billing, users, and webhook management.

Credential Access

High
Category
Privilege Escalation
Content
elif args.command == "login":
        result = login(args.email, args.password)
        print(json.dumps(result, indent=2))
        print(f"\n--- Login successful. Access token expires in {result.get('expires_in', '?')}s ---")


if __name__ == "__main__":
Confidence
95% confidence
Finding
On successful login, the script prints the entire JSON response, which may include an access token even though the message only references its expiration. Emitting bearer tokens to stdout can leak credentials through terminal history, process capture, logs, or CI output, enabling unauthorized access to the Didit account and administrative APIs.

Lp3

Medium
Category
MCP Least Privilege
Confidence
97% confidence
Finding
The skill documents capabilities that rely on environment access, network access, and likely MCP/tooling, but it does not declare any explicit allowed-tools or permission scope. In an agent environment, that omission can let the skill be invoked with broader runtime authority than users or reviewers expect, especially because it performs account creation, credential retrieval, billing, webhook management, and destructive deletions.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The activation language is extremely broad, including phrases like 'handle any platform administration,' which could cause the skill to trigger for loosely related admin/support requests. In this context that is risky because the skill exposes powerful actions such as API key handling, billing top-ups, webhook reconfiguration, manual approval/decline, and destructive deletion.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests

# 1. Register (any email, no business email required)
requests.post("https://apx.didit.me/auth/v2/programmatic/register/",
    json={"email": "you@gmail.com", "password": "MyStr0ng!Pass"})

# 2. Check email for 6-char OTP, then verify → get 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.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests

# 1. Register (any email, no business email required)
requests.post("https://apx.didit.me/auth/v2/programmatic/register/",
    json={"email": "you@gmail.com", "password": "MyStr0ng!Pass"})

# 2. Check email for 6-char OTP, then verify → get 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.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests

# 1. Register (any email, no business email required)
requests.post("https://apx.didit.me/auth/v2/programmatic/register/",
    json={"email": "you@gmail.com", "password": "MyStr0ng!Pass"})

# 2. Check email for 6-char OTP, then verify → get 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.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests

# 1. Register (any email, no business email required)
requests.post("https://apx.didit.me/auth/v2/programmatic/register/",
    json={"email": "you@gmail.com", "password": "MyStr0ng!Pass"})

# 2. Check email for 6-char OTP, then verify → get 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.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests

# 1. Register (any email, no business email required)
requests.post("https://apx.didit.me/auth/v2/programmatic/register/",
    json={"email": "you@gmail.com", "password": "MyStr0ng!Pass"})

# 2. Check email for 6-char OTP, then verify → get 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.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests

# 1. Register (any email, no business email required)
requests.post("https://apx.didit.me/auth/v2/programmatic/register/",
    json={"email": "you@gmail.com", "password": "MyStr0ng!Pass"})

# 2. Check email for 6-char OTP, then verify → get 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.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests

# 1. Register (any email, no business email required)
requests.post("https://apx.didit.me/auth/v2/programmatic/register/",
    json={"email": "you@gmail.com", "password": "MyStr0ng!Pass"})

# 2. Check email for 6-char OTP, then verify → get api_key
Confidence
70% 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
json={"email": "you@gmail.com", "password": "MyStr0ng!Pass"})

# 2. Check email for 6-char OTP, then verify → get api_key
resp = requests.post("https://apx.didit.me/auth/v2/programmatic/verify-email/",
    json={"email": "you@gmail.com", "code": "A3K9F2"})
api_key = resp.json()["application"]["api_key"]
headers = {"x-api-key": api_key, "Content-Type": "application/json"}
Confidence
70% 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
headers = {"x-api-key": api_key, "Content-Type": "application/json"}

# 3. Create a KYC workflow
wf = requests.post("https://verification.didit.me/v3/workflows/",
    headers=headers,
    json={"workflow_label": "My KYC", "workflow_type": "kyc",
          "is_liveness_enabled": True, "is_face_match_enabled": True}).json()
Confidence
70% 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
"is_liveness_enabled": True, "is_face_match_enabled": True}).json()

# 4. Create a session → send user to the URL
session = requests.post("https://verification.didit.me/v3/session/",
    headers=headers,
    json={"workflow_id": wf["uuid"], "vendor_data": "user-123"}).json()
print(f"Send user to: {session['url']}")
Confidence
70% 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
**Response (201):** Workflow object with `uuid`.

```python
wf = requests.post("https://verification.didit.me/v3/workflows/",
    headers={"x-api-key": API_KEY, "Content-Type": "application/json"},
    json={"workflow_label": "KYC + AML", "workflow_type": "kyc",
          "is_liveness_enabled": True, "is_face_match_enabled": True,
Confidence
70% 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
95% confidence
Finding
The markdown describes permanent session deletion and bulk deletion, including a delete_all option, without strong user-facing warnings, confirmation gates, or safety preconditions. Because this platform handles identity-verification records, accidental or induced deletion can cause irreversible loss of compliance-relevant data and operational disruption.

External Transmission

Medium
Category
Data Exfiltration
Content
| `options` | array | Conditional | Required for `multiple_choice`/`checkbox` |

```python
requests.post("https://verification.didit.me/v3/questionnaires/",
    headers=headers,
    json={
        "title": "Employment Details",
Confidence
70% 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
The user deletion operation allows batch deletion and delete_all semantics without clearly emphasizing irreversibility and downstream consequences. In an identity-verification context this can erase user-linked records, disrupt onboarding/review workflows, and create compliance, support, and auditability issues.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This code builds a payload containing potentially sensitive values such as vendor_data, callback, language, and metadata, then sends it via requests.post to an external service. Although the module docstring describes the command usage, it does not clearly warn that these fields will be transmitted off-system, which meets the missing-disclosure criterion for network calls carrying user or system data.

Static analysis

No suspicious patterns detected.