Back to skill

Security audit

gitea-commit-report-skills

Security checks for vulnerabilities and agentic risk

Overview

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.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (6)

T01 · Skill Instruction Hijacking

Error
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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/render_email.py:124
Finding

Unescaped repository and AI content enables stored HTML injection in report emails

Content
View full analysis
📁 {fname}   -{fdel} """ commit_items += f"""
🕐 {time_str}
{msg}
{files_html}
""" ``` ```python risk_lines = "".join( f'
{line}
\n' for line in risk_notes.strip().splitlines() if line.strip() ) ``` ### Technical Analysis The renderer inserts repository names, usernames, branch names, commit messages, filenames, dates, and AI-generated fields directly into HTML templates. It does not use `html.escape`, an auto-escaping template engine, or an HTML sanitizer. These fields cross two untrusted boundaries: - Repository contributors can control commit messages, filenames, branches, and some identity fields. - Prompt-influenced AI output controls overview, suggestion, summary, and risk text. Because the values are inserted into element bodies and link attributes without contextual escaping, an attacker can introduce HTML elements and attributes. Email clients commonly restrict scripts, but many still render links, images, and substantial HTML formatting. Consequently, script blocking does not eliminate phishing, content spoofing, or tracking risks. ...[truncated 1223 chars]

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/render_email.py:12
Finding

Missing configuration silently inserts an undeclared plaintext external IP into report links

Content
View full analysis
{gitea_url}/{repo} ``` ### Technical Analysis The documented Gitea URL is stored in `~/.config/gitea-routine-report/.env`, but `render_email.py` does not load that file. The rendering commands documented in `skill.md` launch a fresh Python process. Unless `GITEA_URL` has separately been exported into that process environment, the renderer silently substitutes the hard-coded endpoint `http://43.156.243.152:3000`. This endpoint is not declared as part of the Skill's required external communication and uses unencrypted HTTP. The resulting report may display and link to a host unrelated to the configured Gitea installation. There is no evidence that the renderer itself transmits data to the IP. The exposure occurs when the generated email is delivered and a recipient follows the link. ### Attack Path 1. The user places `GITEA_URL` in the documented `.env` file without exporting it globally. 2. The report-generation scripts load the file and successfully contact the configured Gitea server. 3. The separate renderer process does not load the `.env`. 4. `render_email.py` selects the hard-coded HTTP IP as its fallback. 5. The outgoing email contains links to that external host. 6. A recipient clicks the apparently trusted project link and connects to the unintended endpoint. ### Impact Assessment Recipients may be redirected to an unintended service, creating phishing and trust-confusion risks. If a recipient follows the link, the destination may receive the recipient's IP address, request time, brow ...[truncated 225 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
skill.md:151
Finding

Predictable shared temporary files permit report disclosure, tampering, and symlink attacks

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding

Dependency installation uses unbounded versions without integrity verification

Content
View full analysis
=2.31.0 python-dotenv>=1.0.0 ``` ```bash python -m pip install --upgrade pip pip install -r "$REQ_FILE" ``` ### Technical Analysis The requirements specify only minimum versions. Future installations may therefore resolve package releases that did not exist when the Skill was reviewed. The setup process also upgrades pip and installs dependencies without hashes or a locked artifact set. The packages are obtained from pip's configured index, so the exact supply-chain exposure depends on the host's pip configuration. The package names shown are established projects, and there is no evidence of typosquatting or a currently malicious package. The confirmed weakness is that installation is not reproducible and does not cryptographically bind the reviewed package versions. Python package installation executes package build or installation logic under the privileges of the user running `setup.sh`. ### Attack Path 1. A future package release, package-index compromise, malicious mirror, or unsafe local pip configuration supplies an unexpected artifact satisfying the broad version constraint. 2. The user runs `setup.sh`. 3. pip resolves and downloads the unreviewed artifact. 4. Installation or later import executes package-controlled code. 5. That code runs with the permissions of the installing or executing user and may access the Skill environment, including the Gitea token. ### Impact Assessment A compromised dependency can execute arbitrary code with the privileges of the user running setup or the report. This could expose the Gitea token, modify generated reports, access user-readable files, or perform network activity. The audit did not identify an actually compromised dependency; the risk arises from the unpinned and unhashed instal ...[truncated 19 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
setup.sh:26
Finding

Token-bearing environment file is created without explicit restrictive permissions

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (49)

Tainted flow: 'collab_url' from os.getenv (line 95, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/generate_report.py (reported line 96)May include surrounding context.

python
# 方式一:获取仓库协作者
    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", ""))

Tainted flow: 'repo_url' from os.getenv (line 102, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/generate_report.py (reported line 103)May include surrounding context.

python
# 方式二:加上 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:

Tainted flow: 'url' from os.getenv (line 116, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/generate_report.py (reported line 118)May include surrounding context.

python
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

Tainted flow: 'url' from os.getenv (line 116, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/generate_report.py (reported line 124)May include surrounding context.

python
# 遍历所有提交找该成员最新的
    params_all = {"limit": 50, "page": 1}
    resp_all = requests.get(url, headers=headers, params=params_all)
    if resp_all.status_code != 200:
        return None

Tainted flow: 'url' from os.getenv (line 15, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/get_admin_emails.py (reported line 16)May include surrounding context.

python
"""获取仓库创建人(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 ""

Tainted flow: 'url' from os.getenv (line 37, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/get_commits.py (reported line 39)May include surrounding context.

python
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", [])

Tainted flow: 'url' from os.getenv (line 37, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/get_commits.py (reported line 137)May include surrounding context.

python
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", [])

Tainted flow: 'url' from os.getenv (line 37, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/get_commits.py (reported line 85)May include surrounding context.

python
# 获取所有分支
    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

Tainted flow: 'commits_url' from os.getenv (line 94, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/get_commits.py (reported line 101)May include surrounding context.

python
}
        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"]

Tainted flow: 'diff_url' from os.getenv (line 153, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/get_commits.py (reported line 154)May include surrounding context.

python
# 获取文件变动详情
        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

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

声明描述的是一个完整流程:获取 Gitea 提交记录、调用 AI 生成进度报告、再发送 HTML 邮件给管理员。但该代码块实际只负责拼接命令并执行本地 Python 脚本 scripts/generate_report.py,解析其 JSON 输出后返回给外部系统。注释和返回文本都明确表示“AI 分析和发邮件由 OpenClaw 根据 skill.md 指示完成”。因此,这段代码并未实现声明中的两个关键能力:AI 报告生成与邮件发送。虽然获取提交数据与声明部分一致,但整体描述夸大了该代码块的实际行为,构成明显不匹配。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

代码的核心行为是:解析时间窗口、获取仓库列表和提交记录、查询管理员邮箱、查询仓库成员/协作者与 owner、计算活跃/不活跃成员及提交统计,然后输出 JSON 结构化摘要。声明中强调的两个主要能力——“调用 AI 生成进度报告”以及“发送 HTML 邮件给仓库管理员”——在该代码片段中均没有出现。虽然代码会获取管理员邮箱,但并未发送邮件;生成的也只是程序化统计摘要,而非可见的 AI 调用结果。因此,声明与实际行为存在实质性不一致。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

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.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

该代码块的核心功能仅是从 Gitea 拉取仓库与提交数据并做格式化输出,属于“提交数据采集”阶段。声明描述的是一个完整流程:获取提交记录、调用 AI 生成进度报告、并发送 HTML 邮件。当前代码中没有任何 AI 服务调用、报告生成、邮件构造或发送、管理员收件人解析等实现,因此实际行为与声明的主要目的存在明显不一致。虽然获取 Gitea 提交记录与声明的第一部分一致,但缺失后两项关键能力,使整体描述不能准确代表此代码块的实际行为。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

声明描述的是一个完整流程型技能:拉取 Gitea 提交记录、调用 AI 生成进度报告、并发送 HTML 邮件。但提供的代码块只负责将输入数据拼装成 HTML 邮件正文,属于邮件渲染子模块。代码没有任何网络请求到 Gitea、没有调用模型或外部 AI 服务、没有 SMTP/邮件 API 发送逻辑,也没有管理员收件人处理。虽然 HTML 邮件与声明中的最终产物相关,但该代码块的实际行为只是支持性模板渲染,无法代表声明中的主要能力,因此存在明显描述-行为不匹配。

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · env-example.txt (reported line 4)May include surrounding context.

text
# 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

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/generate_report.py (reported line 13)May include surrounding context.

python
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}"}

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/get_admin_emails.py (reported line 6)May include surrounding context.

python
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}"}

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/get_commits.py (reported line 10)May include surrounding context.

python
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}"}

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · setup.sh (reported line 9)May include surrounding context.

sh
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}"}

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · setup.sh (reported line 44)May include surrounding context.

sh
- name: GITEA_TOKEN
    type: personal_access_token
    provider: gitea
environment_file: ~/.config/gitea-routine-report/.env
---

# Gitea Routine Report

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · setup.sh (reported line 47)May include surrounding context.

sh
- name: GITEA_TOKEN
    type: personal_access_token
    provider: gitea
environment_file: ~/.config/gitea-routine-report/.env
---

# Gitea Routine Report

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · skill.md (reported line 15)May include surrounding context.

md
- name: GITEA_TOKEN
    type: personal_access_token
    provider: gitea
environment_file: ~/.config/gitea-routine-report/.env
---

# Gitea Routine Report

Missing User Warnings

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

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.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
main.js:45