T09 · Insecure Skill Coding Practices
- Location
scripts/x_client.py:604- Finding
State-changing X operations execute without the declared user confirmation
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This X helper is mostly coherent, but it needs review because many account-changing X actions can run without enforced confirmation and OAuth material is cached in plaintext.
Install only if you are comfortable granting this skill broad control over an X account. Use the least-privileged token possible, avoid sensitive accounts, review every generated command before running it, and treat ~/.x-helper/auth.json as a secret if you use OAuth authorization.
scripts/x_client.py:604State-changing X operations execute without the declared user confirmation
scripts/x_auth.py:159OAuth client secret and unnecessary PKCE material are stored in plaintext
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
for attempt in range(3):
req = urllib.request.Request(url, data=data, headers=headers, method=method)
try:
with urllib.request.urlopen(req, timeout=30) as resp:
raw = resp.read().decode()
return json.loads(raw) if raw else {}
except URLError as e:
The declared description presents a full-featured X assistant with extensive end-user capabilities. The supplied code chunk does not implement those features; instead, it serves as a diagnostic/setup checker for the environment and authentication state. While this may be a supporting component of such a skill, the actual behavior of this chunk is materially different from the declared primary purpose. It also accesses local auth data and runs a subprocess to validate authentication, which are not reflected in the description. Therefore this code chunk does not accurately match the declared description.
The description claims a comprehensive X assistant with many user-facing platform operations. The supplied code does not implement those actions; it is a narrow auth helper responsible for OAuth PKCE flow, token exchange, token persistence, status reporting, and credential deletion. While authentication is a plausible supporting component of an X assistant, the declared purpose does not accurately represent this chunk's actual primary behavior. Therefore this chunk is materially mismatched with the stated description.
The feature coverage largely matches the declared purpose: the code genuinely acts as an X assistant with search, posting, threads, DMs, Lists, bookmarks, trends, articles, block/mute, and more. There is no obvious unrelated or hidden capability beyond X API interactions. The main mismatch is in the implementation description: it is not strictly 'Pure Python stdlib, no dependencies' because this file depends on non-stdlib modules x_auth and x_media. In addition, the auth description in _get_token says this skill never requests, stores, or refreshes tokens itself and only uses X_BEARER_TOKEN, yet the CLI advertises auth authorize/status/logout commands that rely on another module, making the authentication behavior only partially represented by this code chunk. So the primary purpose matches, but the declared self-contained/stdlib-only representation is materially inaccurate.
The description claims a full-featured X assistant with many capabilities, but the supplied code only implements media uploading. While 'post with media' is related, this chunk does not perform posting itself and does not implement the many other declared capabilities. There is no evidence of undeclared malicious behavior; the issue is that the declared purpose is materially broader than the actual behavior of this code chunk.
Referenced artifact was not completely inspected
python3 scripts/x_client.py auth authorize --client-id YOUR_CLIENT_ID
Referenced artifact was not completely inspected
python3 scripts/x_client.py auth authorize --client-id YOUR_CLIENT_ID
Referenced artifact was not completely inspected
python3 scripts/x_client.py auth authorize --client-id YOUR_CLIENT_ID
Referenced artifact was not completely inspected
python3 scripts/x_client.py auth authorize --client-id YOUR_CLIENT_ID
Referenced artifact was not completely inspected
python3 scripts/x_client.py auth authorize --client-id YOUR_CLIENT_ID
Referenced artifact was not completely inspected
python3 scripts/x_client.py auth authorize --client-id YOUR_CLIENT_ID
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# ── Auth ──────────────────────────────────────────────────────────────────
def _get_token():
"""Get the access token.
The token comes ONLY from the user-set X_BEARER_TOKEN env var. The user
manages this credential themselves (e.g. `export X_BEARER_TOKEN=...`);
Without declared permissions the skill's intent is opaque and cannot be validated.
The skill documentation, triggers, and behavioral rules are written entirely in Chinese, which implicitly enforces a specific language for interaction and understanding. The file does not indicate that users may choose another language or that the Chinese locale is a required, justified constraint for a region-specific tool.
The documentation makes conflicting claims about whether cached OAuth tokens are automatically read and refreshed from local JSON storage. This can cause operators to make unsafe assumptions about where credentials reside and when they are used, increasing the risk of inadvertent credential exposure, persistence, or use in automation contexts handling sensitive data.
Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.
if auth_ok:
print(f" 用户: @{auth.get('username')} ({auth.get('user_name')})")
expires = auth.get("expires_at", 0)
remaining = max(0, expires - int(__import__('time').time()))
print(f" Token 剩余: {remaining // 60} 分钟")
else:
missing = []
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
print()
print(" 测试认证...")
try:
result = subprocess.run(
[sys.executable, CLIENT_SCRIPT, "auth", "status"],
capture_output=True, text=True, timeout=10
)
The helper requests a broad set of write-capable scopes plus offline.access, which increases blast radius if the stored token is stolen or misused. In an agent skill that can act on a user's social account, overbroad OAuth scope violates least privilege and makes any downstream compromise more damaging.
The helper requests a broad set of write-capable scopes plus offline.access, which increases blast radius if the stored token is stolen or misused. In an agent skill that can act on a user's social account, overbroad OAuth scope violates least privilege and makes any downstream compromise more damaging.
The code persists OAuth credentials, including access/refresh token material and potentially client_secret, to disk automatically without explicit user warning or consent. Although file permissions are restricted, local malware, backup/sync leakage, shared-account usage, or later accidental exposure can lead to account takeover or long-lived unauthorized access due to offline.access.
The client includes a confirmation helper for irreversible actions, but tweet deletion does not use it before issuing a DELETE request. In an agent or scripted context, this increases the chance of accidental destructive actions from prompt mistakes, argument confusion, or automation errors.
The _get_token docstring says the skill 'never requests, stores, or refreshes tokens on the user's behalf,' presenting a no-auth-management intent. But cmd_auth exposes authorize, status, and logout flows via x_auth, and the usage banner instructs users to run auth authorize to obtain a token, which directly contradicts that statement.
List deletion is irreversible and is performed immediately without any explicit confirmation gate. In an agent-operated skill, that makes unintended destructive actions more likely, especially if commands are generated automatically from natural-language requests.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
import urllib.parse
from urllib.error import URLError
MEDIA_UPLOAD_URL = "https://api.x.com/2/media/upload"
FILE_SIZE_LIMIT_SIMPLE = 5 * 1024 * 1024 # 5 MB for simple upload
MAX_RETRIES = 5
POLL_INTERVAL = 2
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
import urllib.parse
from urllib.error import URLError
MEDIA_UPLOAD_URL = "https://api.x.com/2/media/upload"
FILE_SIZE_LIMIT_SIMPLE = 5 * 1024 * 1024 # 5 MB for simple upload
MAX_RETRIES = 5
POLL_INTERVAL = 2