T09 · Insecure Skill Coding Practices
- Location
lib/README_TOKEN_MANAGER.md:112- Finding
Hardcoded Ezviz Application Credentials in Distributed Documentation
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill matches its camera-capture purpose, but it exposes sensitive Ezviz credentials, access tokens, and full signed camera-image URLs in documentation or default output.
Install only if you are comfortable with this skill handling camera credentials and temporary image URLs. Rotate the hardcoded-looking Ezviz credentials if they were ever real, use a dedicated least-privilege Ezviz app, avoid command-line secrets, disable token caching in shared environments, and do not send default output to shared logs until full pic_url values are redacted.
lib/README_TOKEN_MANAGER.md:112Hardcoded Ezviz Application Credentials in Distributed Documentation
scripts/device_capture.py:419Complete Signed Camera-Image URLs Are Exposed in Process Output
lib/token_manager.py:78Predictable Shared Temporary Token Cache Can Expose or Corrupt Access Tokens
scripts/device_capture.py:237Unrestricted Download of Server-Supplied URLs Enables SSRF and Resource Exhaustion
The declared description centers on batch screenshot/grab functionality for multiple Ezviz devices, with token management as a supporting feature. However, the actual code chunk contains only token acquisition and cache management logic. It calls the Ezviz token endpoint, stores tokens on disk in a global cache, supports refresh/list/clear operations, and exposes a CLI for these cache actions. There is no code for enumerating devices, requesting live images, grabbing snapshots, downloading files, or handling batch capture workflows. While token management is mentioned in the description, here it is the entire primary behavior rather than a supporting implementation detail, so the code does not accurately represent the declared skill purpose.
核心功能与声明大体一致:代码面向萤石设备批量抓图,支持多设备、Token 缓存/管理以及可选本地下载,这些都与描述相符。但声明中的安全要求写明“必须设置 EZVIZ_APP_KEY 和 EZVIZ_APP_SECRET 环境变量”,而实际代码并非必须:它会在环境变量缺失时回退读取用户主目录下多个 OpenClaw 配置文件中的凭证,还允许从命令行参数接收 appKey/appSecret。这属于对凭证来源和所访问资源的实质性扩展,且未在声明中体现。虽然代码注释说明了配置文件优先级低于环境变量,但声明没有说明会读取本地配置文件或命令行秘密输入,因此存在描述与行为不一致。
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
cat /tmp/ezviz_global_token_cache/global_token_cache.json
rm -rf /tmp/ezviz_global_token_cache/
### 验证命令
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
cat /tmp/ezviz_global_token_cache/global_token_cache.json
rm -rf /tmp/ezviz_global_token_cache/
### 验证命令
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
cat /tmp/ezviz_global_token_cache/global_token_cache.json
rm -rf /tmp/ezviz_global_token_cache/
### 验证命令
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
cat /tmp/ezviz_global_token_cache/global_token_cache.json
rm -rf /tmp/ezviz_global_token_cache/
### 验证命令
The skill recommends storing credentials in a local .env file, which creates a plaintext secret at rest on disk. Even with chmod 600, this can be exposed through backups, accidental commits, shell history around creation steps, or compromise of the local user account.
# 推荐:使用 .env 文件(不要提交到版本控制)
echo "EZVIZ_APP_KEY=your_key" >> .env
echo "EZVIZ_APP_SECRET=your_secret" >> .env
chmod 600 .env
This line continues the pattern of writing sensitive Ezviz credentials into a plaintext .env file. The risk is not the literal filename alone, but the operational practice of encouraging disk persistence of secrets without stronger controls.
# 推荐:使用 .env 文件(不要提交到版本控制)
echo "EZVIZ_APP_KEY=your_key" >> .env
echo "EZVIZ_APP_SECRET=your_secret" >> .env
chmod 600 .env
Although chmod 600 reduces file exposure, it does not eliminate the risk of storing live API credentials in plaintext on disk. In shared or monitored environments, local compromise or backup leakage can still reveal the credentials.
# 推荐:使用 .env 文件(不要提交到版本控制)
echo "EZVIZ_APP_KEY=your_key" >> .env
echo "EZVIZ_APP_SECRET=your_secret" >> .env
chmod 600 .env
# 加载环境变量
Sourcing a .env file normalizes the use of plaintext credential files and may also execute unintended shell content if the file is modified or generated unsafely. In a security-sensitive skill handling camera credentials, encouraging shell sourcing adds unnecessary risk beyond simple environment injection.
# 推荐:使用 .env 文件(不要提交到版本控制)
echo "EZVIZ_APP_KEY=your_key" >> .env
echo "EZVIZ_APP_SECRET=your_secret" >> .env
chmod 600 .env
# 加载环境变量
source .env
This finding still relates to the preceding '.env' workflow and the recommendation to load credentials from a local plaintext file into the shell environment. That practice expands credential exposure compared with managed secret injection and is especially sensitive for surveillance-device access.
chmod 600 .env
source .env
### 3. 禁用缓存(高安全场景)
The README includes what appear to be full Ezviz app credentials in test commands, which creates a direct secret-exposure risk if those values are real or were ever valid. In a security-sensitive token manager, publishing example app keys/secrets materially increases the chance of unauthorized API access, token issuance, account abuse, and secret reuse across environments.
The CLI prints the first 30 characters of the access token to stdout, which can expose sensitive credentials in terminal history, CI logs, shell capture, or monitoring systems. Because this skill manages reusable API tokens for camera access, even partial disclosure materially increases the chance of credential compromise and unauthorized device access, especially if token formats are predictable or logs are broadly accessible.
result = get_cached_token(args.app_key, args.app_secret, use_cache=use_cache)
if result["success"]:
print(f"\nAccess Token: {result['access_token'][:30]}...")
print(f"Expires: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(result['expire_time'] / 1000))}")
print(f"From Cache: {result['from_cache']}")
else:
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def get_access_token(app_key, app_secret, use_cache=None):
"""
Get access token using global token manager.
Args:
app_key: App key
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def get_access_token(app_key, app_secret, use_cache=None):
"""
Get access token using global token manager.
Args:
app_key: App key
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def get_access_token(app_key, app_secret, use_cache=None):
"""
Get access token using global token manager.
Args:
app_key: App key
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def get_access_token(app_key, app_secret, use_cache=None):
"""
Get access token using global token manager.
Args:
app_key: App key
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def get_access_token(app_key, app_secret, use_cache=None):
"""
Get access token using global token manager.
Args:
app_key: App key
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def get_access_token(app_key, app_secret, use_cache=None):
"""
Get access token using global token manager.
Args:
app_key: App key
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def get_access_token(app_key, app_secret, use_cache=None):
"""
Get access token using global token manager.
Args:
app_key: App key
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def get_access_token(app_key, app_secret, use_cache=None):
"""
Get access token using global token manager.
Args:
app_key: App key
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def get_access_token(app_key, app_secret, use_cache=None):
"""
Get access token using global token manager.
Args:
app_key: App key
The skill declares capabilities that clearly involve environment access, file reads/writes, network calls, and shell execution, but it does not define an explicit tool scope such as permissions or allowed-tools. This increases the attack surface because an agent or reviewer cannot easily enforce least privilege or understand what the skill is permitted to do before execution.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
# 推荐:使用 .env 文件(不要提交到版本控制)
echo "EZVIZ_APP_KEY=your_key" >> .env
echo "EZVIZ_APP_SECRET=your_secret" >> .env
chmod 600 .env
# 加载环境变量
source .env
The README explicitly demonstrates printing the access token and shows command examples that include app secrets on the command line, which can expose sensitive values through terminal history, process listings, logs, screenshots, and copied output. In the context of a shared token manager for camera devices, leaked tokens or secrets could enable unauthorized image capture or API operations across multiple integrated skills.
No suspicious patterns detected.