T05 · Unauthorized Access and Privilege Escalation
- Location
scripts/apply_permissions.py:35- Finding
Feishu Application Receives Permissions Beyond the Skill's Operational Requirements
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill appears to do Feishu accounting setup and bookkeeping, but it asks for broad Feishu access and handles App Secrets in ways that can leak them.
Install only if you are comfortable granting this skill broad Feishu Base permissions and storing a long-lived App Secret locally. Use a least-privilege Feishu app if possible, avoid pasting secrets into chat/logs, rotate any secret already used with this workflow, verify or build the APK yourself, and manually confirm parsed receipt data before recording it.
scripts/apply_permissions.py:35Feishu Application Receives Permissions Beyond the Skill's Operational Requirements
scripts/setup_bitable.py:107App Secret Is Exposed Through Command Arguments, Standard Output, Chat, Plaintext Files, and APK Local Storage
SKILL.md:124Mutable Prebuilt APK Is Recommended Without Integrity or Provenance Verification
scripts/cleanup_bitable.py:67Bulk Cleanup Utility Irreversibly Deletes Remote Records Without Confirmation or Dry-Run Protection
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"
data = json.dumps({"app_id": FEISHU_APP_ID, "app_secret": FEISHU_APP_SECRET}).encode("utf-8")
req = urllib.request.Request(url, data=data, headers={"Content-Type": "application/json"})
with urllib.request.urlopen(req, timeout=15) as resp:
result = json.loads(resp.read())
if result.get("code") != 0:
raise RuntimeError(f"获取 Tenant Token 失败: {result}")
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
"Authorization": f"Bearer {token}", "Content-Type": "application/json",
}, method="POST")
try:
with urllib.request.urlopen(req, timeout=15) as resp:
result = json.loads(resp.read())
if result.get("code") == 0:
return {"success": True}
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
"Authorization": f"Bearer {token}", "Content-Type": "application/json",
}, method="POST")
try:
with urllib.request.urlopen(req, timeout=15) as resp:
result = json.loads(resp.read())
if result.get("code") == 0:
return {"success": True}
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
url_fields = (f"https://open.feishu.cn/open-apis/base/v3/bases/{base}"
f"/tables/{tbl}/fields")
req_fields = urllib.request.Request(url_fields, headers={"Authorization": f"Bearer {token}"})
with urllib.request.urlopen(req_fields, timeout=15) as resp:
fields_data = json.loads(resp.read())
field_map = {}
for f in fields_data.get("data", {}).get("fields", []):
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
f"/tables/{tbl}/records/{rid}")
del_req = urllib.request.Request(del_url, method="DELETE",
headers={"Authorization": f"Bearer {token}"})
with urllib.request.urlopen(del_req, timeout=15) as resp:
del_r = json.loads(resp.read())
if del_r.get("code") == 0:
return {"success": True, "record_id": rid}
The overview claims a complete two-stage package and emphasizes a permanent-token synchronization model, but the supplied behavior centers on setup/admin actions and dynamic token retrieval. This inconsistency is security-relevant because it obscures actual privileged operations and can defeat informed consent during deployment.
The overview claims a complete two-stage package and emphasizes a permanent-token synchronization model, but the supplied behavior centers on setup/admin actions and dynamic token retrieval. This inconsistency is security-relevant because it obscures actual privileged operations and can defeat informed consent during deployment.
The overview claims a complete two-stage package and emphasizes a permanent-token synchronization model, but the supplied behavior centers on setup/admin actions and dynamic token retrieval. This inconsistency is security-relevant because it obscures actual privileged operations and can defeat informed consent during deployment.
The overview claims a complete two-stage package and emphasizes a permanent-token synchronization model, but the supplied behavior centers on setup/admin actions and dynamic token retrieval. This inconsistency is security-relevant because it obscures actual privileged operations and can defeat informed consent during deployment.
The documentation directs plaintext handling of App Secret and other credentials without adequate warnings about sensitivity, storage risks, or least-privilege alternatives. This is dangerous because users may normalize insecure secret management for tokens that can authorize broad Feishu operations.
The skill creates a natural-language credential exposure workflow by telling the agent to restate user-supplied secrets and save them locally in plaintext. This is more dangerous than ordinary config handling because chat transcripts and agent logs become additional secret exposure surfaces.
The explicit instruction to create a .env file containing App ID, App Secret, and table tokens is direct credential access and storage in plaintext. If the host, repository, backups, or logs are exposed, these credentials can be reused to access or modify Feishu resources.
# 在技能目录创建 .env(record_bill.py 会自动从同目录读取)
cat > /path/to/feishu-accounting/.env << 'EOF'
FEISHU_APP_ID=你的App_ID
FEISHU_APP_SECRET=你的App_Secret
FEISHU_BASE_TOKEN=你的Base_Token
The usage triggers are overly broad and allow normal phrases or even image upload alone to initiate bookkeeping actions. Because the skill can write local files and sync to Feishu, loose triggering materially increases the risk of unauthorized or accidental data modification.
The skill explicitly instructs automatic writes based on OCR/vision results from receipts without any confirmation warning. In financial software, this is especially risky because model mistakes directly alter user records and could be abused with crafted images to poison data.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# ❌ 错误:setdefault 不覆盖已存在的环境变量
# 如果父 shell 已有 FEISHU_APP_ID(旧应用的),.env 的值会被忽略
os.environ.setdefault(k.strip(), v.strip())
# ✅ 正确:直接赋值,.env 始终优先
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# ❌ 错误:setdefault 不覆盖已存在的环境变量
# 如果父 shell 已有 FEISHU_APP_ID(旧应用的),.env 的值会被忽略
os.environ.setdefault(k.strip(), v.strip())
# ✅ 正确:直接赋值,.env 始终优先
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
```python
# ❌ 错误:setdefault 不覆盖已存在的环境变量
# 如果父 shell 已有 FEISHU_APP_ID(旧应用的),.env 的值会被忽略
os.environ.setdefault(k.strip(), v.strip())
# ✅ 正确:直接赋值,.env 始终优先
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
```python
# ❌ 错误:setdefault 不覆盖已存在的环境变量
# 如果父 shell 已有 FEISHU_APP_ID(旧应用的),.env 的值会被忽略
os.environ.setdefault(k.strip(), v.strip())
# ✅ 正确:直接赋值,.env 始终优先
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def get_tenant_token(app_id: str, app_secret: str) -> str:
"""获取 Tenant Access Token"""
url = f"{FEISHU_HOST}/open-apis/auth/v3/tenant_access_token/internal"
data = json.dumps({"app_id": app_id, "app_secret": app_secret}).encode("utf-8")
req = urllib.request.Request(
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def get_tenant_token(app_id: str, app_secret: str) -> str:
"""获取 Tenant Access Token"""
url = f"{FEISHU_HOST}/open-apis/auth/v3/tenant_access_token/internal"
data = json.dumps({"app_id": app_id, "app_secret": app_secret}).encode("utf-8")
req = urllib.request.Request(
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def get_tenant_token(app_id: str, app_secret: str) -> str:
"""获取 Tenant Access Token"""
url = f"{FEISHU_HOST}/open-apis/auth/v3/tenant_access_token/internal"
data = json.dumps({"app_id": app_id, "app_secret": app_secret}).encode("utf-8")
req = urllib.request.Request(
The script automatically loads secrets from a .env file located relative to the skill directory and injects them into the process environment without validating file ownership, permissions, or trust boundary. In an agent skill context, this increases the chance of credential misuse or unintended secret exposure, especially if the skill directory is shared, synced, or writable by other components.
from datetime import date, datetime
from pathlib import Path
# ── 自动加载 skill 目录下的 .env ──────────────────────────────────────────
_SKILL_ENV = Path(__file__).parent.parent / ".env"
if _SKILL_ENV.exists():
for line in _SKILL_ENV.read_text().splitlines():
The .env path is hardcoded to the parent skill directory, encouraging local plaintext credential storage in a predictable location. Predictable, automatic secret loading makes it easier for other local processes, backups, or repository mistakes to expose Feishu credentials that grant remote data access.
from pathlib import Path
# ── 自动加载 skill 目录下的 .env ──────────────────────────────────────────
_SKILL_ENV = Path(__file__).parent.parent / ".env"
if _SKILL_ENV.exists():
for line in _SKILL_ENV.read_text().splitlines():
line = line.strip()
The script prints the Feishu App Secret directly to stdout, which can leak into terminal scrollback, shell history capture, CI/CD logs, remote session transcripts, or agent telemetry. In an agent skill context, stdout is often collected and forwarded, making secret disclosure more dangerous than in an isolated local script.
The machine-readable JSON includes the App Secret, which enables downstream systems, logs, parsers, or other agents to ingest and potentially persist the credential automatically. Because this output is specifically structured for AI parsing, it materially increases the likelihood of silent credential propagation and compromise.