T01 · Skill Instruction Hijacking
- Location
scripts/get_commits.py:151- Finding
Repository-controlled content can perform prompt injection against the AI agent
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill mostly does what it says, but it can broadly collect private repository activity and email HTML reports without clear confirmation or enough safeguards against injected content.
Install only if you trust the Gitea server and token scope, use a read-only token limited to the intended repositories, confirm recipients and repository scope before any email is sent, and review/fix HTML escaping, temp-file handling, the hard-coded fallback URL, dependency pinning, and .env permissions before using it in a shared or sensitive environment.
scripts/get_commits.py:151Repository-controlled content can perform prompt injection against the AI agent
scripts/render_email.py:124Unescaped repository and AI content enables stored HTML injection in report emails
scripts/render_email.py:12Missing configuration silently inserts an undeclared plaintext external IP into report links
skill.md:151Predictable shared temporary files permit report disclosure, tampering, and symlink attacks
requirements.txt:1Dependency installation uses unbounded versions without integrity verification
setup.sh:26Token-bearing environment file is created without explicit restrictive permissions
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
# 方式一:获取仓库协作者
collab_url = f"{GITEA_URL}/api/v1/repos/{repo_full_name}/collaborators"
resp = requests.get(collab_url, headers=headers)
if resp.status_code == 200:
for u in resp.json():
members.add(u.get("login", ""))
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
# 方式二:加上 owner 本人
repo_url = f"{GITEA_URL}/api/v1/repos/{repo_full_name}"
resp2 = requests.get(repo_url, headers=headers)
if resp2.status_code == 200:
owner = resp2.json().get("owner", {}).get("login", "")
if owner:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
headers = {"Authorization": f"token {GITEA_TOKEN}"}
url = f"{GITEA_URL}/api/v1/repos/{repo_full_name}/commits"
params = {"limit": 1, "page": 1}
resp = requests.get(url, headers=headers, params=params)
if resp.status_code != 200:
return None
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
# 遍历所有提交找该成员最新的
params_all = {"limit": 50, "page": 1}
resp_all = requests.get(url, headers=headers, params=params_all)
if resp_all.status_code != 200:
return None
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
"""获取仓库创建人(owner)的注册邮箱"""
owner = repo_full_name.split("/")[0]
url = f"{GITEA_URL}/api/v1/users/{owner}"
response = requests.get(url, headers=HEADERS)
if response.status_code == 200:
return response.json().get("email", "")
return ""
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
while True:
url = f"{GITEA_URL}/api/v1/repos/search"
params = {"limit": 50, "page": page}
response = requests.get(url, headers=HEADERS, params=params)
if response.status_code != 200:
break
data = response.json().get("data", [])
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
while True:
url = f"{GITEA_URL}/api/v1/repos/search"
params = {"limit": 50, "page": page}
response = requests.get(url, headers=HEADERS, params=params)
if response.status_code != 200:
break
data = response.json().get("data", [])
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
# 获取所有分支
url = f"{GITEA_URL}/api/v1/repos/{repo_full_name}/branches"
response = requests.get(url, headers=HEADERS, params={"limit": 50})
if response.status_code != 200:
return sha_to_branch
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
}
if since:
params["since"] = since.strftime("%Y-%m-%dT%H:%M:%SZ")
branch_resp = requests.get(commits_url, headers=HEADERS, params=params)
if branch_resp.status_code == 200:
for commit in branch_resp.json():
sha = commit["sha"]
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
# 获取文件变动详情
diff_url = f"{GITEA_URL}/api/v1/repos/{repo_full_name}/git/commits/{sha}"
diff_resp = requests.get(diff_url, headers=HEADERS)
files = []
total_additions = 0
total_deletions = 0
声明描述的是一个完整流程:获取 Gitea 提交记录、调用 AI 生成进度报告、再发送 HTML 邮件给管理员。但该代码块实际只负责拼接命令并执行本地 Python 脚本 scripts/generate_report.py,解析其 JSON 输出后返回给外部系统。注释和返回文本都明确表示“AI 分析和发邮件由 OpenClaw 根据 skill.md 指示完成”。因此,这段代码并未实现声明中的两个关键能力:AI 报告生成与邮件发送。虽然获取提交数据与声明部分一致,但整体描述夸大了该代码块的实际行为,构成明显不匹配。
代码的核心行为是:解析时间窗口、获取仓库列表和提交记录、查询管理员邮箱、查询仓库成员/协作者与 owner、计算活跃/不活跃成员及提交统计,然后输出 JSON 结构化摘要。声明中强调的两个主要能力——“调用 AI 生成进度报告”以及“发送 HTML 邮件给仓库管理员”——在该代码片段中均没有出现。虽然代码会获取管理员邮箱,但并未发送邮件;生成的也只是程序化统计摘要,而非可见的 AI 调用结果。因此,声明与实际行为存在实质性不一致。
The supplied code only retrieves the repository owner's email address from the Gitea API based on the repo name. While obtaining an admin email could be a supporting step for sending reports, this chunk does not implement the declared primary functionality: it neither reads commit history, nor generates a report, nor sends email. It also accesses user email information, which is a sensitive/resource-specific capability not explicitly reflected in the description. Therefore, the code chunk does not accurately represent the declared behavior on its own.
该代码块的核心功能仅是从 Gitea 拉取仓库与提交数据并做格式化输出,属于“提交数据采集”阶段。声明描述的是一个完整流程:获取提交记录、调用 AI 生成进度报告、并发送 HTML 邮件。当前代码中没有任何 AI 服务调用、报告生成、邮件构造或发送、管理员收件人解析等实现,因此实际行为与声明的主要目的存在明显不一致。虽然获取 Gitea 提交记录与声明的第一部分一致,但缺失后两项关键能力,使整体描述不能准确代表此代码块的实际行为。
声明描述的是一个完整流程型技能:拉取 Gitea 提交记录、调用 AI 生成进度报告、并发送 HTML 邮件。但提供的代码块只负责将输入数据拼装成 HTML 邮件正文,属于邮件渲染子模块。代码没有任何网络请求到 Gitea、没有调用模型或外部 AI 服务、没有 SMTP/邮件 API 发送逻辑,也没有管理员收件人处理。虽然 HTML 邮件与声明中的最终产物相关,但该代码块的实际行为只是支持性模板渲染,无法代表声明中的主要能力,因此存在明显描述-行为不匹配。
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# Gitea server base URL (no trailing slash)
GITEA_URL=https://your-gitea.example.com
# Personal access token with read repository permissions
GITEA_TOKEN=your_gitea_token_here
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
import json
from dotenv import load_dotenv
load_dotenv(os.path.expanduser("~/.config/gitea-routine-report/.env"))
GITEA_URL = os.getenv("GITEA_URL")
GITEA_TOKEN = os.getenv("GITEA_TOKEN")
HEADERS = {"Authorization": f"token {GITEA_TOKEN}"}
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
import json
from dotenv import load_dotenv
load_dotenv(os.path.expanduser("~/.config/gitea-routine-report/.env"))
GITEA_URL = os.getenv("GITEA_URL")
GITEA_TOKEN = os.getenv("GITEA_TOKEN")
HEADERS = {"Authorization": f"token {GITEA_TOKEN}"}
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
import json
from dotenv import load_dotenv
load_dotenv(os.path.expanduser("~/.config/gitea-routine-report/.env"))
GITEA_URL = os.getenv("GITEA_URL")
GITEA_TOKEN = os.getenv("GITEA_TOKEN")
HEADERS = {"Authorization": f"token {GITEA_TOKEN}"}
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
import json
from dotenv import load_dotenv
load_dotenv(os.path.expanduser("~/.config/gitea-routine-report/.env"))
GITEA_URL = os.getenv("GITEA_URL")
GITEA_TOKEN = os.getenv("GITEA_TOKEN")
HEADERS = {"Authorization": f"token {GITEA_TOKEN}"}
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
- name: GITEA_TOKEN
type: personal_access_token
provider: gitea
environment_file: ~/.config/gitea-routine-report/.env
---
# Gitea Routine Report
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
- name: GITEA_TOKEN
type: personal_access_token
provider: gitea
environment_file: ~/.config/gitea-routine-report/.env
---
# Gitea Routine Report
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
- name: GITEA_TOKEN
type: personal_access_token
provider: gitea
environment_file: ~/.config/gitea-routine-report/.env
---
# Gitea Routine Report
The skill description and flow do not require a clear user warning or confirmation before sending repository-derived reports by email to repository administrators. This is dangerous because the action is external, potentially discloses project activity summaries and member information, and may occur based on implicit defaults rather than informed user consent.
Without declared permissions the skill's intent is opaque and cannot be validated.
Detected: suspicious.dangerous_exec