T09 · Insecure Skill Coding Practices
- Location
scripts/gitea_api.py:14- Finding
Payload-Controlled Gitea Endpoint Exposes Bot Credentials and Sensitive Meeting Data
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill matches its meeting-ingestion purpose, but it needs review because task data can redirect Gitea uploads carrying the bot token and meeting content.
Install only in an environment where backend task payloads are trusted or validated. Before use, require the Gitea endpoint to come from administrator-controlled configuration or an allowlist, use a bot token limited to the intended KB repository, keep the shared work directory private, and make sure users understand that full meeting minutes/transcripts may be archived into the repository.
scripts/gitea_api.py:14Payload-Controlled Gitea Endpoint Exposes Bot Credentials and Sensitive Meeting Data
scripts/utils.py:114Sensitive Meeting Context Cache Uses Ambient Filesystem Permissions
The skill's effective write scope is much broader than simple meeting ingestion: it allows updates across many KB roots and performs generic manifest-driven repository writes. In a security context, that breadth matters because a compromised or mistaken upstream agent could use this pathway to modify unrelated knowledge pages, causing integrity loss well beyond the intended meeting-import function.
The skill's effective write scope is much broader than simple meeting ingestion: it allows updates across many KB roots and performs generic manifest-driven repository writes. In a security context, that breadth matters because a compromised or mistaken upstream agent could use this pathway to modify unrelated knowledge pages, causing integrity loss well beyond the intended meeting-import function.
The skill's effective write scope is much broader than simple meeting ingestion: it allows updates across many KB roots and performs generic manifest-driven repository writes. In a security context, that breadth matters because a compromised or mistaken upstream agent could use this pathway to modify unrelated knowledge pages, causing integrity loss well beyond the intended meeting-import function.
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.
payload_path = root / "payload.json"
payload_path.write_text(json.dumps(payload), encoding="utf-8")
command = [sys.executable, "-B", str(SCRIPTS / "run_task.py")]
env = dict(os.environ)
for key in ("GITEA_URL", "GITEA_BOT_TOKEN", "GITEA_BOT_USERNAME", "GITEA_ORG"):
env.pop(key, None)
env["RESEARCH_KB_DRY_RUN"] = "true"
The skill describes capabilities that require broad access to environment variables, local files, shell execution, network calls, and repository writes, but it does not declare any explicit tool scope or permission boundaries. That creates a least-privilege failure: an operator or agent may invoke a skill with more authority than users expect, increasing the blast radius if the skill is misused or its downstream inputs are adversarial.
The apply phase performs real writes to Gitea and local KB metadata, but the skill lacks a prominent up-front warning in its description/UX that execution is state-changing. This can lead users or orchestrators to treat the skill as a read/analysis tool and accidentally authorize destructive or unintended repository changes.
The helper is a generic external-command bridge rather than a narrowly scoped integration for Tencent Meeting ingestion. In the context of an agent skill, that creates unnecessary privilege expansion: any actor who can affect the configured command can cause the skill to run unrelated programs, potentially leading to arbitrary code execution, data exfiltration, or abuse of the agent host.
This code executes an external process via subprocess.run using a command string that is split at runtime and augmented with extra arguments. Although shell injection is reduced by avoiding shell=True, the bridge still grants arbitrary command-execution capability if the configured command or appended arguments can be influenced by untrusted skill configuration or upstream inputs, which is broader than necessary for the stated ingestion task.
if not command:
raise RuntimeError("外部 skill 命令未配置")
args = shlex.split(command) + list(extra_args or [])
completed = subprocess.run(
args,
text=True,
encoding="utf-8",
This code invokes an external command via subprocess.run, which is a safety-relevant operation because it executes code outside the current process. There is no confirmation prompt, logging/print disclosure, or explanatory comment/docstring near the execution to warn users that an external command will be run.
The skill performs remote file creation or modification via Gitea API PUT requests in upsert_text and upsert_bytes, which can change repository contents. There is no confirmation prompt, print/log disclosure, or inline documentation warning the user before these write operations occur.
The _request method sends HTTP requests containing an authorization token and, for write operations, uploads file contents to the configured Gitea server. The file includes no user-visible notice, prompt, or warning that data and credentials will be transmitted over the network.
The file embeds Chinese user-facing strings such as "相关概念", "相关资源", and related section headings, and later injects them into generated markdown content. This enforces a specific language/locale in output without any visible opt-in or justification, which matches the language/locale policy-violation category.
The code repeatedly calls external Tencent Meeting tools to retrieve records and transcript content, which involves transmitting identifiers and processing meeting data. There is no visible user notification, confirmation, or inline disclosure in this file that such remote data access will occur.
The code persists full AI minutes and full meeting transcripts into repository-managed Markdown files via archive_meeting_source() and render_source_file(). Meeting recordings and transcripts often contain sensitive business, personal, or regulated data, so automatically committing them into a KB/repo can cause broad overexposure, long-term retention, and unintended access by anyone with repository visibility.
The requiredSections and rules in meeting_page_instructions() are entirely specified in Chinese, effectively constraining generated page structure and instructions to one language. There is no indication of user opt-in or support for alternate languages/locales.
This bridge executes a command taken directly from environment variables and then forwards tool-call arguments to that external process. If an attacker can influence the runtime environment or installation layout, they can redirect execution to a malicious program and exfiltrate or tamper with sensitive meeting recordings, transcripts, and metadata. The skill context increases risk because the delegated tool handles high-sensitivity meeting content.
The function builds a payload containing tool arguments and client metadata, then invokes an external command via run_external_command. Although this appears to be part of the bridge's purpose, the code provides no confirmation prompt, print/log disclosure, or comment/docstring warning that user or system data will be passed to another skill process.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
env.pop(key, None)
env["RESEARCH_KB_DRY_RUN"] = "true"
validated = subprocess.run(
command + ["validate-manifest", "--input", str(payload_path), "--context", str(context_path)],
check=True,
capture_output=True,
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
)
self.assertTrue(json.loads(validated.stdout.decode("utf-8"))["validationOnly"])
applied = subprocess.run(
command + ["apply", "--input", str(payload_path), "--context", str(context_path)],
check=True,
capture_output=True,
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
context_path = root / "context.json"
payload_path.write_text(json.dumps(payload), encoding="utf-8")
context_path.write_text(json.dumps(context, ensure_ascii=False), encoding="utf-8")
completed = subprocess.run(
[
sys.executable,
"-B",
The exception messages are written in Chinese, which imposes a specific language on users without any visible opt-in or indication that the skill is intentionally locale-specific. This can violate language/locale policy when the skill otherwise provides no user choice or documented regional constraint.
The RuntimeError message is presented entirely in Chinese, which imposes a specific language on users without opt-in or fallback. Under the language/locale policy, user-facing text should not force a locale unless the skill is explicitly documented as region-specific or offers language selection.
No suspicious patterns detected.