Back to skill

Security audit

Yunshang Aifei Cli Share

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real OA client, but it has broad production account access and security weaknesses that could leak session/API keys or cause unintended business changes.

Review before installing. Use only with a dedicated low-privilege OA account and a dedicated model-provider key, avoid the raw command, do not run production write actions without separate confirmation, and fix HTTPS transport plus protected token storage before handling sensitive business, HR, or finance data.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
aifei_api.py:115
Finding
OA bearer token disclosure through unrestricted absolute URLs<![CDATA[ ## Vulnerability Details **File Location**: `aifei_api.py:115-127`; `aifei.py:145-161` **Vulnerability Type**: Authenticated server-side request redirection and credential disclosure **Risk Level**: High ### Vulnerable Code ```python def _url(self, path, use_biz_prefix=False): if path.startswith('http'): return path if path.startswith('/dev-api') or path.startswith('/prod-api'): return f'{self.base_url}{path}' prefix = self.config.get('api_prefix_biz', self.config['api_prefix']) if use_biz_prefix else self.config['api_prefix'] return f'{self.base_url}{prefix}/{path.lstrip("/")}' def _headers(self): h = {'Content-Type': 'application/json;charset=UTF-8'} if self.token: h['Authorization'] = f'Bearer {self.token}' return h ``` ```python def cmd_raw(client, args): method = args.method.upper() path = args.path data = json.loads(args.data) if args.data else None if method == 'GET': result = client.get(path, biz=args.biz) elif method == 'POST': result = client.post(path, data, biz=args.biz) elif method == 'PUT': result = client.put(path, data, biz=args.biz) else: print(f'Unsupported method: {method}') return print(json.dumps(result, ensure_ascii=False, indent=2)[:2000]) ``` ### Technical Analysis The raw API command accepts a caller-supplied path, while `_url()` explicitly permits values beginning with `http`. The request methods subsequently attach the current OA bearer token through `_headers()` regardless of the destination. Consequently, an absolute URL can redirect an authenticated request away from the intended OA servers. This behavior is unnecessary for the declared OA client functionality because legitimate operations only require requests to the two configured OA origins. The check also accepts both HTTP and HTTPS URLs without parsing or validating the destination hostname, port, scheme, or resolved address. ### Attac ...[truncated 980 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove support for absolute URLs from `_url()`. 2. Accept only relative OA API paths. 3. Parse the final URL with `urllib.parse.urlparse()` and require an exact allowlisted tuple of scheme, hostname, and port. 4. Require HTTPS for every authenticated request. 5. Attach authentication headers only after confirming that the destination is an approved OA origin. 6. Disable redirects or validate every redirect target before forwarding credentials. 7. Restrict or remove the raw API command from normal Skill operation. 8. Add tests proving that external URLs, scheme-relative URLs, encoded hostnames, and unapproved ports are rejected. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
aifei_api.py:80
Finding
Login credentials and bearer tokens transmitted over plaintext HTTP<![CDATA[ ## Vulnerability Details **File Location**: `aifei_api.py:80-96`, `aifei_api.py:123-145`, and `aifei_api.py:187-196` **Vulnerability Type**: Cleartext transmission of authentication material and sensitive OA data **Risk Level**: Critical ### Vulnerable Code ```python return { 'test': { 'base_url': 'http://192.168.24.25', 'api_prefix': '/dev-api', 'username': username, 'password': password, 'domain': '192.168.24.25', 'login_mode': 'api', }, 'prod': { 'base_url': 'http://192.168.24.208:20080', 'api_prefix': '/dev-api', 'api_prefix_biz': '/prod-api', 'username': username, 'password': password, 'domain': '192.168.24.208', 'login_mode': 'api', }, } ``` ```python def _headers(self): h = {'Content-Type': 'application/json;charset=UTF-8'} if self.token: h['Authorization'] = f'Bearer {self.token}' return h def get(self, path, params=None, biz=False): url = self._url(path, use_biz_prefix=biz) resp = self.session.get(url, params=params, headers=self._headers(), timeout=30) return parse_response(resp.text) def post(self, path, data=None, biz=False, encrypt=True): url = self._url(path, use_biz_prefix=biz) body = None if data is not None and encrypt: body = sm4_encrypt(json.dumps(data, ensure_ascii=False)) elif data is not None: body = json.dumps(data, ensure_ascii=False) resp = self.session.post(url, data=body, headers=self._headers(), timeout=30) return parse_response(resp.text) ``` ```python login_data = { 'username': self.config['username'], 'password': sm4_encrypt(self.config['password']), 'code': captcha, 'uuid': code_resp['uuid'], 'forceLogin': '1' } result = self.post(f'{self.config["api_prefix"]}/auth/login', login_data) ``` ### Technical Analysis Both production and test origins use plaintext HTTP. The client sends bearer tokens in the `Aut ...[truncated 1765 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Expose both OA environments exclusively through HTTPS. 2. Validate certificates against a trusted enterprise CA and never disable certificate verification. 3. Redirecting HTTP to HTTPS is insufficient for credentials; the client itself must begin with an HTTPS URL. 4. Replace the client-distributed static encryption scheme with a server-approved authentication protocol protected by TLS. 5. Rotate passwords and invalidate bearer tokens that may have crossed the plaintext connection. 6. Configure cookies with `Secure`, `HttpOnly`, and appropriate `SameSite` protections where server architecture permits. 7. Consider certificate pinning or an enterprise mutual-TLS design for highly sensitive internal deployments. 8. Add a startup check that rejects authenticated requests whose parsed URL scheme is not `https`. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
aifei_api.py:156
Finding
Reusable OA session tokens stored in plaintext with default file permissions<![CDATA[ ## Vulnerability Details **File Location**: `aifei_api.py:156-169` **Vulnerability Type**: Insecure local credential storage **Risk Level**: High ### Vulnerable Code ```python def _set_token(self, token): self.token = token self.session.cookies.set('Admin-Token', token, domain=self.config['domain'], path='/') self._token_file.write_text(json.dumps({ 'token': token, 'time': time.time(), 'env': self.env_name })) def _load_cached_token(self): if self._token_file.exists(): try: data = json.loads(self._token_file.read_text()) if time.time() - data.get('time', 0) < 14400: self._set_token(data['token']) info = self.get('system/user/getInfo') ``` ### Technical Analysis The client writes a reusable bearer token into `.token-test.json` or `.token-prod.json` in the project directory. The token is stored as plaintext JSON, and the code does not explicitly create the file with owner-only permissions. The project directory may be synchronized, backed up, packaged, indexed, shared with other local users, or accidentally committed to version control. Reading the file is sufficient to acquire the session credential; knowledge of the user's password is unnecessary. Calling `_set_token()` while loading the cache also rewrites the token file without adding stronger protection. ### Attack Path 1. A victim logs in through the Skill. 2. `_set_token()` writes the bearer token to the project directory. 3. A local user, malware process, backup service, workspace-sharing mechanism, or accidental repository commit obtains the token file. 4. The attacker extracts the `token` field. 5. The attacker replays it against OA APIs before expiration or revocation. ### Impact Assessment The attacker receives the same active OA permissions as the victim account for the token's remaining lifetime. This can expose business, financial, employee, project, and personal information and may permi ...[truncated 165 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Store session credentials in the operating system's credential manager rather than the project directory. 2. If file storage is unavoidable, create a private application-data directory outside the source tree. 3. Create the file atomically with owner-only permissions such as mode `0600`. 4. Verify ownership and permissions before reading an existing cache. 5. Add token files to `.gitignore`, packaging exclusions, backup exclusions, and workspace-sharing exclusions. 6. Delete expired or invalid token files rather than retaining them. 7. Use shorter-lived, revocable tokens and refresh-token rotation where the server supports it. 8. Provide an explicit logout operation that revokes the server-side token and securely removes the local cache. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
modules/captcha_solver.py:27
Finding
CAPTCHA module accesses Agent-wide provider credentials beyond least privilege<![CDATA[ ## Vulnerability Details **File Location**: `modules/captcha_solver.py:27-50` and `modules/captcha_solver.py:101-120` **Vulnerability Type**: Cross-scope credential discovery and external transmission **Risk Level**: High ### Vulnerable Code ```python def _find_api_config() -> tuple: key = os.getenv('DASHSCOPE_API_KEY') base = os.getenv('DASHSCOPE_BASE_URL', '') if key: if not base: base = 'https://dashscope.aliyuncs.com/compatible-mode/v1' return key, base openclaw_paths = [ Path.home() / '.openclaw' / 'openclaw.json', Path(__file__).parent.parent.parent.parent / 'openclaw.json', ] for config_path in openclaw_paths: if config_path.exists(): try: config = json.loads(config_path.read_text(encoding='utf-8')) providers = config.get('models', {}).get('providers', {}) for name, provider in providers.items(): base_url = provider.get('baseUrl', '') if 'dashscope' in base_url or 'alibaba' in name: api_key = provider.get('apiKey', '') if api_key and api_key != '__OPENCLAW_REDACTED__': return api_key, base_url except Exception: pass ``` ```python response = requests.post( f'{base_url}/chat/completions', json={ 'model': CAPTCHA_MODEL, 'messages': [{ 'role': 'user', 'content': [ {'type': 'image_url', 'image_url': {'url': f'data:image/png;base64,{image_base64}'}}, {'type': 'text', 'text': 'Recognize the arithmetic CAPTCHA and return the expression and answer.'} ] }] }, headers={ 'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json' }, timeout=15 ) ``` ### Technical Analysis When a Skill-scoped API key is absent, the module searches glob ...[truncated 1966 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove all automatic reads of `~/.openclaw/openclaw.json` and other Agent-wide configuration files. 2. Require a dedicated Skill-scoped API key with the minimum provider permissions and a separate usage quota. 3. Use a fixed official HTTPS DashScope endpoint or enforce an exact hostname and port allowlist. 4. Reject user-info components, redirects, non-HTTPS schemes, unexpected ports, and hostname suffix tricks. 5. Do not forward the authorization header across redirects. 6. Clearly disclose that CAPTCHA images are sent to an external service and obtain user or administrator approval. 7. Prefer an approved local arithmetic-CAPTCHA recognizer where feasible to avoid expanding the authentication trust boundary. 8. Rotate the global provider key if this Skill has already accessed it in an untrusted environment. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding
Third-party dependencies are installed without version or integrity constraints<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1-3` **Vulnerability Type**: Unpinned and unverifiable dependency resolution **Risk Level**: Medium ### Vulnerable Code ```text gmssl requests python-dotenv ``` ### Technical Analysis All dependencies are specified without exact versions or package hashes. Each installation can therefore resolve to a different future release from the configured package index. No evidence shows that the listed package names are typosquatted or currently malicious. The confirmed weakness is that the build is not reproducible and does not enforce the identity of reviewed dependency artifacts. A compromised upstream account, malicious release, dependency substitution, or incompatible update could be installed automatically. Because Python package installation may execute build-system code, compromise can occur during installation before the Skill itself is run. ### Attack Path 1. A user follows the documented dependency installation procedure. 2. The package installer resolves the latest versions available from its configured index. 3. An upstream package, maintainer account, release artifact, or package index is compromised. 4. The unconstrained requirement resolves to the affected release. 5. Malicious build or runtime code executes with the installing user's privileges. 6. That code may access OA credentials, cached tokens, Agent configuration, and other files available to the user. ### Impact Assessment A compromised dependency may execute with the full privileges of the user installing or running the Skill. This could expose OA passwords, bearer tokens, DashScope credentials, OpenClaw configuration, and other local data. The issue does not demonstrate a presently malicious dependency, so exploitation depends on a supply-chain compromise or unsafe package source. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin every direct dependency to a reviewed exact version. 2. Generate and commit a lock file containing reviewed transitive versions. 3. Use hash checking, such as `pip install --require-hashes`, to verify downloaded artifacts. 4. Install only from an approved package index or internal mirror. 5. Review dependency provenance, release history, licenses, and known vulnerabilities. 6. Automate vulnerability scanning while requiring explicit review before dependency updates. 7. Build and test dependencies in an isolated environment with minimal access to credentials and production systems. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (42)

Credential Access

High
Category
Privilege Escalation
Content
复制 `.env.example` 为 `.env`,填入你的云上艾飞账号:

```bash
cp .env.example .env
```

```ini
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The `raw` command accepts arbitrary paths and methods and forwards them directly through the authenticated client, effectively exposing a generic API proxy to the operator. In a skill intended for curated business queries, this bypasses any command-level safety, least-privilege scoping, or business logic restrictions and can be used to access or modify unintended resources.

Credential Access

High
Category
Privilege Escalation
Content
"""
云上艾飞 API 客户端(分享版)
SM4 加解密 + Cookie Token 认证
账号密码从 .env 文件读取,不含任何硬编码敏感信息
"""

import sys
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
"""
云上艾飞 API 客户端(分享版)
SM4 加解密 + Cookie Token 认证
账号密码从 .env 文件读取,不含任何硬编码敏感信息
"""

import sys
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
"""
云上艾飞 API 客户端(分享版)
SM4 加解密 + Cookie Token 认证
账号密码从 .env 文件读取,不含任何硬编码敏感信息
"""

import sys
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
"""
云上艾飞 API 客户端(分享版)
SM4 加解密 + Cookie Token 认证
账号密码从 .env 文件读取,不含任何硬编码敏感信息
"""

import sys
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
"""
云上艾飞 API 客户端(分享版)
SM4 加解密 + Cookie Token 认证
账号密码从 .env 文件读取,不含任何硬编码敏感信息
"""

import sys
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
"""
云上艾飞 API 客户端(分享版)
SM4 加解密 + Cookie Token 认证
账号密码从 .env 文件读取,不含任何硬编码敏感信息
"""

import sys
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
from dotenv import load_dotenv

# 加载 .env
load_dotenv(Path(__file__).parent / '.env')

# 确保 UTF-8 输出
if sys.stdout.encoding != 'utf-8':
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

eval() call detected

High
Category
Dangerous Code Execution
Content
# 验算
        op_map = {'+': '+', '-': '-', '×': '*', 'x': '*', 'X': '*', '*': '*', '÷': '/', '/': '/'}
        try:
            calc = str(int(eval(f'{num1}{op_map.get(op, op)}{num2}')))
            if calc != model_answer:
                print(f'[验证码] ⚠️ 模型={model_answer} 计算={calc},使用计算结果')
                return calc
Confidence
85% confidence
Finding
Direct eval() call evaluates arbitrary expressions. This can be exploited to execute malicious code or exfiltrate data.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The README states that captcha solving is performed automatically via an AI model and references use of a third-party API key, but it does not clearly disclose what data is transmitted to that external service during login. Even if only the captcha image is sent, this occurs in an authentication flow and may expose sensitive login-adjacent data or create compliance/privacy concerns for users who assume the tool is fully internal.

Intent-Code Divergence

Medium
Confidence
91% confidence
Finding
Line L003 explicitly claims the skill has 'zero browser dependency' as a pure API client. Later, the documented file structure lists `login.py` as 'Playwright 登录(备用)', which contradicts that claim because Playwright is a browser automation dependency.

Intent-Code Divergence

Medium
Confidence
93% confidence
Finding
The top-level description states there is no browser dependency. However, the dependency section explicitly lists Playwright for fallback login, which directly contradicts the claim of zero browser dependence rather than merely omitting an implementation detail.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill documents write-capable operations such as weekly comments and task creation against a production OA system without any warning, confirmation requirement, or safety guidance. In an agent setting, that materially increases the risk of unintended remote state changes, spam, workflow disruption, or unauthorized business actions.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The skill is presented as an OA system API client, but it also documents a DingTalk robot webhook push that sends content to an external third-party service. This expands the skill's data-flow surface and creates a clear exfiltration/notification channel for internal OA data without any stated guardrails, consent prompts, or scope limitation.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The DingTalk webhook example transmits message content and user mentions to an external service, yet the documentation provides no warning about outbound data sharing or notification impact. This can lead to accidental disclosure of internal information and unsolicited paging of real users.

External Transmission

Medium
Category
Data Exfiltration
Content
```python
import requests
webhook = 'https://oapi.dingtalk.com/robot/send?access_token=daf5fd...'
requests.post(webhook, json={
    'msgtype': 'markdown',
    'markdown': {'title': '标题', 'text': '@康楠 内容...'},
    'at': {'atMobiles': ['手机号'], 'isAtAll': False}
Confidence
97% confidence
Finding
The documented `requests.post(webhook, json=...)` performs a direct external network transmission to DingTalk, creating an outbound channel that can carry OA-derived content, mentions, and potentially sensitive business data. In the context of an internal OA client, this is especially risky because it bridges internal system access with external messaging in a single skill.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The top-level CLI docstring lists several read-only business commands but omits the `raw` subcommand that can perform arbitrary API calls, including state-changing requests. This mismatch hides a materially more powerful capability from users and reviewers, increasing the risk of misuse and reducing informed consent around what the tool can do.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The `raw` subcommand allows arbitrary POST and PUT requests with user-supplied JSON and executes them immediately, with no warning, dry-run mode, or confirmation prompt. That makes accidental or impulsive state-changing operations much more likely, especially against production because the default environment is `prod`.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The module docstring explicitly claims there is no hardcoded sensitive information, but the file contains a fixed SM4 key in source code. A hardcoded cryptographic key is sensitive because anyone with code access can decrypt protected payloads or forge encrypted requests, and the misleading comment may cause reviewers to overlook the exposure.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The client writes the bearer token to a local JSON file in the script directory without setting restrictive permissions, encryption, or warning the user. If another local user, process, backup system, or accidental commit accesses that file, the token can be reused to impersonate the account until expiry.

Session Persistence

Medium
Category
Rogue Agent
Content
{
              "method": "POST",
              "path": "/contractRecoupPlan/recoupPlanList",
              "methodName": "recoupList",
              "comment": "查询合同回款计划列表"
            },
            {
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
{
              "method": "POST",
              "path": "/contractRecoupPlan/recoupPlanList",
              "methodName": "recoupList",
              "comment": "查询合同回款计划列表"
            },
            {
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
{
              "method": "POST",
              "path": "/contractRecoupPlan/recoupPlanList",
              "methodName": "recoupList",
              "comment": "查询合同回款计划列表"
            },
            {
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
{
              "method": "POST",
              "path": "/contractRecoupPlan/recoupPlanList",
              "methodName": "recoupList",
              "comment": "查询合同回款计划列表"
            },
            {
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
modules/captcha_solver.py:75