T09 · Insecure Skill Coding Practices
- Location
scripts/load_credentials.py:117- Finding
Application Secret Stored in Plaintext and Exposed Through an Unrestricted Retrieval Command
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill is purpose-aligned, but it stores and exposes powerful credentials too casually for a tool that gates sensitive operations.
Review before installing. This skill is not obviously malicious, but it handles app secrets, phone numbers, and approvals for destructive or secret-revealing actions. Use only in a controlled workspace, avoid committing the memory directory, rotate exposed appKey values, and do not rely on this alone as a strong approval boundary for high-impact operations.
scripts/load_credentials.py:117Application Secret Stored in Plaintext and Exposed Through an Unrestricted Retrieval Command
scripts/request_authorization.py:191Application Secrets and Phone Numbers Are Exposed Through Process Command-Line Arguments
scripts/request_authorization.py:24Phone Numbers Are Protected with Deterministic AES-ECB and MD5-Based Key Derivation
scripts/request_authorization.py:125Authorization Approval Is Not Bound to the Requested Sensitive Operation
scripts/request_authorization.py:136Arbitrary Redirect and Callback URLs Are Forwarded to the Authorization Service
scripts/poll_authorization.py:109HTTP Requests Have No Per-Request Deadline, Allowing Authorization Workflows to Block Indefinitely
The skill instructs storing the long-lived appId and especially appKey in a plaintext memory file at memory/cmcc-digital-credential.json for reuse across future sensitive operations. Persisting reusable secrets in an agent-accessible file materially increases the chance of credential disclosure through other skills, logs, backups, or filesystem access, and those credentials can then be abused to request or spoof authorization flows.
appId: Application ID (24 characters)
appKey: Application secret keymemory/cmcc-digital-credential.json:
{
"appId": "...",
This example reinforces and normalizes the practice of persisting credentials to a predictable local file path, which can encourage insecure operational handling of secrets. Even though the example masks appKey in the displayed output, the underlying behavior still leaves the real secret stored on disk where it may be accessible to other components or users.
appKey: *** (hidden) appName: Javis (predefined) templateId: qfx9pkizs42up7y61jsehs9v8e1xms4m (predefined) Stored to: memory/cmcc-digital-credential.json
### Example 2: Phase 2 - Performing Sensitive Operation
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
import re
import sys
MEMORY_PATH = "memory/cmcc-digital-credential.json"
def parse_credentials_file(file_content: str) -> dict:
The credential file format requires users to supply fields named 智能体DID and 智能体密钥, and nearby headings/instructions also switch to Chinese, but the document does not state that the skill is China-region-only or provide an alternative language/locale option. This creates a language/locale policy issue because the skill effectively imposes a specific language convention without user opt-in or clear justification.
The script persists sensitive credentials to a predictable plaintext JSON file on disk without warning the user or applying any protection such as restrictive permissions or encryption. In a skill context, stored appKey material can be recovered later by other local users, processes, backups, or accidental commits, enabling unauthorized use of the credential.
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.
load_parser = subparsers.add_parser('load', help='Load credentials from file')
load_parser.add_argument('file', help='Path to credential file')
load_parser.add_argument('--force', '-f', action='store_true',
help='Overwrite existing credentials without confirmation')
# Check command
check_parser = subparsers.add_parser('check', help='Check if credentials exist')
The get command prints the full credential object when no field is specified, which includes appKey in plaintext. This can leak secrets to terminal history, logs, screenshots, calling wrappers, or other systems that capture stdout, making credential compromise straightforward.
The script accepts the application secret key via the --appKey command-line argument, which can expose the credential through shell history, process listings, audit logs, and orchestration tooling. In a credential/authorization polling skill, this is more dangerous because the secret is directly used to generate authentication signatures for API requests, so disclosure could enable unauthorized API access or impersonation of the client application.
The documentation for load_credentials_from_file, save_credentials_to_memory, and load_credentials_from_memory says the credential dictionary includes templateId, but the actual parser only returns appId and appKey and no code injects templateId before saving or returning data. This is an active contradiction between the inline documentation and runtime behavior, not just an omission.
The script's user-facing description, help text, success messages, and error messages are all hardcoded in English. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale restriction is explicitly documented and justified, which it is not here.
No suspicious patterns detected.