Back to skill

Security audit

调用senseaudio asr的课堂转译助手,将英文课堂录音、讲座视频、组会音频等内容自动转写为文本,生成中文总结

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it claims, but it has broad network fetching and file-writing behavior that needs review before use.

Install only if you are comfortable uploading recordings and generated notes to the configured services. Avoid using untrusted page URLs, avoid custom OpenAI-compatible base URLs unless you control them, use a limited test Obsidian vault or skip Obsidian export, and keep API tokens scoped and private.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
Findings (4)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/classroom_note_translator.py:116
Finding

Unrestricted URL Fetching Enables Server-Side Request Forgery

Content
View full analysis
str: resp = requests.get(page_url, headers=DEFAULT_HTTP_HEADERS, timeout=30) if resp.status_code >= 400: raise RuntimeError(f"Failed to fetch page {page_url}: {resp.status_code}") ``` ```python def download_media_file(media_url: str, temp_dir: Path) -> Tuple[Path, bool]: resp = requests.get(media_url, headers=DEFAULT_HTTP_HEADERS, stream=True, timeout=180) if resp.status_code >= 400: raise RuntimeError(f"Failed to download media {media_url}: {resp.status_code}") content_type = resp.headers.get("Content-Type", "") ext = extension_from_url(media_url) or content_type_to_extension(content_type) if not ext: ext = ".mp4" if infer_is_video(media_url, content_type) else ".mp3" file_path = temp_dir / f"page_media{ext}" with file_path.open("wb") as f: for chunk in resp.iter_content(chunk_size=1024 * 1024): if chunk: f.write(chunk) return file_path, infer_is_video(str(file_path), content_type) ``` ```python temp_dir_obj = tempfile.TemporaryDirectory(prefix="classroom_note_translator_page_") temp_dir = Path(temp_dir_obj.name) media_url = extract_media_url_from_page(page_url) media_path, is_video = download_media_file(media_url, temp_dir) return media_path, is_video, temp_dir_obj, media_url ``` ### Technical Analysis The `--page-url` value is fetched directly without validating its scheme, hostname, resolved IP address, or destination network. The script then parses attacker-controlled HTML and issues a second request to the selected media URL. Neither request restricts redirects or prevents access to loopback, link-local, private, reserved, or cloud metadata addresses. Consequently, the scri ...[truncated 1896 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/classroom_note_translator.py:451
Finding

Obsidian Folder Path Traversal Allows Writes Outside the Selected Vault

Content
View full analysis
Path: if not vault: raise RuntimeError("Missing Obsidian vault path") target_dir = vault / folder target_dir.mkdir(parents=True, exist_ok=True) note_path = target_dir / f"{slugify(title)}.md" note_path.write_text(markdown_text, encoding="utf-8") return note_path ``` ```python obsidian_result = None if args.export_obsidian: vault_path = Path(args.obsidian_vault).expanduser().resolve() obsidian_result = export_to_obsidian(markdown_text, title, vault_path, args.obsidian_folder) ``` ### Technical Analysis The `--obsidian-folder` argument is joined directly to the resolved vault path without containment validation. A relative value containing `..` can escape the vault, while an absolute `Path` supplied as the folder causes Python path joining to discard the vault prefix entirely. The resulting path is created with `mkdir(parents=True, exist_ok=True)`, and `write_text` overwrites an existing file with the generated Markdown. Although the title-derived filename is sanitized by `slugify`, the parent folder remains attacker-controlled. The declared behavior only requires writing a note inside a user-selected Obsidian vault. Permitting writes elsewhere in the filesystem exceeds that required scope. ### Attack Path 1. An attacker or untrusted caller enables `--export-obsidian`. 2. The caller supplies a writable vault path and sets `--obsidian-folder` to a value such as `../../target-directory` or an absolute directory. 3. Python constructs `vault / folder`; traversal components escape the vault, or an absolute folder replaces the vault prefix. 4. `mkdir` creates the escaped directory hierarchy where permissions permi ...[truncated 891 chars]
Remediation
View remediation

other

Warning
Location
scripts/classroom_note_translator.py:168
Finding

Unbounded Remote Media Download Can Exhaust Host Resources

Content
View full analysis
Tuple[Path, bool]: resp = requests.get(media_url, headers=DEFAULT_HTTP_HEADERS, stream=True, timeout=180) if resp.status_code >= 400: raise RuntimeError(f"Failed to download media {media_url}: {resp.status_code}") content_type = resp.headers.get("Content-Type", "") ext = extension_from_url(media_url) or content_type_to_extension(content_type) if not ext: ext = ".mp4" if infer_is_video(media_url, content_type) else ".mp3" file_path = temp_dir / f"page_media{ext}" with file_path.open("wb") as f: for chunk in resp.iter_content(chunk_size=1024 * 1024): if chunk: f.write(chunk) return file_path, infer_is_video(str(file_path), content_type) ``` ### Technical Analysis The streaming loop writes data until the remote server closes the connection. It does not enforce a maximum cumulative byte count, validate `Content-Length`, or require an approved media MIME type. The request timeout does not provide a reliable total transfer limit: a server that continues to provide data within socket timeout intervals can keep the download active and grow the temporary file indefinitely. After download, content inferred to be video is processed with `ffmpeg`, which can add CPU and memory pressure. File extension and content-type inference are not sufficient security controls because both are controlled by the remote server or URL. ### Attack Path 1. An attacker supplies a page containing a media reference to an attacker-controlled server, or directly controls the media URL extracted by the script. 2. The server returns a successful response and continuously streams data, or advertises a small or absent `Content-Length` while s ...[truncated 952 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding

Unpinned Dependency Installation Is Not Reproducible

Content
View full analysis
=2.31.0 ``` The documented installation process executes the unconstrained requirement: ```bash cd ~/.openclaw/skills/classroom-note-translator python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt ``` ### Technical Analysis The lower-bound requirement permits pip to install any future `requests` release that satisfies `>=2.31.0`, together with whichever transitive dependency versions are selected at installation time. No cryptographic hashes are provided. As a result, an installation performed later may execute a dependency set different from the one reviewed during the audit. This weakens build reproducibility and supply-chain integrity. The reviewed package name is legitimate and there is no evidence of typosquatting or an intentionally malicious dependency; the risk arises from the absence of exact version and integrity controls. ### Attack Path 1. An operator follows the documented setup instructions. 2. Pip resolves the broad `requests>=2.31.0` constraint against the configured package index. 3. Pip selects the newest compatible package and transitive dependency versions available at that time. 4. If a selected release or configured package source is compromised, unreviewed package code is installed and may execute during installation or import. 5. The Skill subsequently imports and runs that dependency with the permissions of the Agent account. ### Impact Assessment A compromised or unexpectedly incompatible dependency could execute with the same privileges as the Skill, including access to: - API keys exposed to the Skill process. - User-selected audio, video, transcript, and output files. - The Agent account's available filesystem and network permissions. No current dependency ...[truncated 133 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (29)

Tainted flow: 'headers' from os.getenv (line 245, credential/environment) → requests.post (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/classroom_note_translator.py (reported line 266)May include surrounding context.

python
with audio_path.open("rb") as f:
        files = {"file": (audio_path.name, f)}
        resp = requests.post(SENSEAUDIO_ENDPOINT, headers=headers, data=data, files=files, timeout=300)
    if resp.status_code >= 400:
        raise RuntimeError(f"SenseAudio API error {resp.status_code}: {resp.text}")
    return resp.json()

Tainted flow: 'base_url' from os.getenv (line 401, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
93% confidence
Finding

The code sends the OpenAI API key to a URL fully controlled by the OPENAI_BASE_URL environment variable. If that variable is modified by an attacker or misconfigured to an untrusted host, the bearer token and full transcript contents will be exfiltrated to a third-party endpoint.

Content

Scanner excerpt · scripts/classroom_note_translator.py (reported line 405)May include surrounding context.

python
if not api_key:
        raise RuntimeError("Missing OPENAI_API_KEY for openai-compatible summarization")

    resp = requests.post(
        base_url.rstrip("/") + "/chat/completions",
        headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
        json={"model": model, "messages": messages, "temperature": 0.2},

Tainted flow: 'token' from os.getenv (line 452, credential/environment) → requests.post (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/classroom_note_translator.py (reported line 479)May include surrounding context.

python
"children": children,
    }

    resp = requests.post(
        "https://api.notion.com/v1/pages",
        headers={
            "Authorization": f"Bearer {token}",

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared behavior understates important capabilities: fetching and downloading media from arbitrary web pages, optionally sending content to an OpenAI-compatible API, and writing output to arbitrary user-specified paths. This mismatch can defeat user consent and policy review because reviewers may approve a transcription skill without realizing it also performs broader network retrieval, third-party transmission, and unrestricted file output.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill invokes shell, reads and writes local files, uses environment secrets, and performs network access, but it does not declare any tool scope or permission boundaries. This increases the risk of over-privileged execution and makes it harder for users or platforms to understand and constrain what the skill can actually do.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

L003-L004 将技能定义为把英文课堂内容转成中文笔记,且示例命令在 L045 固定使用 --target-language zh。这种语言输出约束没有体现用户可选择其他语言或显式确认语言偏好,属于自然语言层面的语言/locale 政策风险。

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill sends classroom audio/video to an external ASR provider but does not clearly warn users about privacy, confidentiality, or possible policy restrictions on uploading recordings. Lecture recordings may contain student voices, names, institutional content, or copyrighted material, so silent external transmission creates meaningful data exposure risk.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
91% confidence
Finding

The skill transmits audio content to an external network endpoint, which is an inherent data egress path. In this context the transmission is expected for ASR, but it still carries privacy and compliance risk because recordings may contain sensitive educational or personal information.

Content

Scanner excerpt · SKILL.md (reported line 103)May include surrounding context.

md
## 注意事项

- SenseAudio 文档显示,`sense-asr` 与 `sense-asr-pro` 支持翻译、句级/字级时间戳、说话人分离与情感分析;HTTP 接口为 `POST https://api.senseaudio.cn/v1/audio/transcriptions`,采用 `multipart/form-data` 与 Bearer Token。
- 若使用 `--page-url`,脚本会先抓取页面 HTML,优先提取 `og:video/og:audio` 和 `<video>/<audio>/<source>` 的 `src`,下载后再处理;部分站点(需登录、DRM、动态脚本拼接媒体地址)可能提取失败。
- 若 Obsidian vault 不可写,则只生成 Markdown 文件,由用户自行导入。
- 若未配置 Notion token,则跳过 Notion 导出。

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · references/setup.md (reported line 12)May include surrounding context.

例如:

bash
mkdir -p ~/.openclaw/skills
cp -R classroom-note-translator-openclaw ~/.openclaw/skills/classroom-note-translator

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This markdown file applies to SQP-2, and it explicitly tells users to inject environment variables including SENSEAUDIO_API_KEY and NOTION_TOKEN into configuration. The surrounding instructions do not warn users that these values are secrets, should be protected from sharing/version control, or will be used for external service access.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The markdown describes --export-notion and a Notion parent page ID, which indicates lecture-derived content will be sent to Notion. There is no accompanying warning about privacy or that user-provided audio/transcript-derived notes may be uploaded to an external service.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/classroom_note_translator.py (reported line 19)May include surrounding context.

python
import requests

SENSEAUDIO_ENDPOINT = "https://api.senseaudio.cn/v1/audio/transcriptions"
DEFAULT_MODEL = "sense-asr-pro"
DEFAULT_SUMMARY_PROVIDER = "local"
DEFAULT_HTTP_HEADERS = {

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill description frames the tool as processing provided classroom audio/video, but the implementation also fetches arbitrary web pages and downloads linked media. That expands the trust boundary and can be abused for unintended remote retrieval, privacy issues, or processing attacker-selected content without clear user understanding.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The code scrapes arbitrary HTML for media URLs and downloads them, which is broader than a simple note-transcription workflow and can pull attacker-controlled content from arbitrary sites. In this context, that capability increases exposure to SSRF-like internal fetching, unwanted bandwidth consumption, and ingestion of maliciously chosen media.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/classroom_note_translator.py (reported line 224)May include surrounding context.

python
"64k",
        str(output_path),
    ]
    proc = subprocess.run(cmd, capture_output=True, text=True)
    if proc.returncode != 0:
        raise RuntimeError(f"ffmpeg failed to extract audio: {proc.stderr.strip()}")
    return output_path

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill uploads user-provided audio, potentially containing sensitive classroom or personal information, to the external SenseAudio service without any in-code user-facing disclosure or consent check. In a note-taking context, silent third-party transmission of recordings is privacy-relevant and can violate user expectations or policy requirements.

Content

No source excerpt is available for this finding.

Tainted flow: 'data' from requests.post (line 413, network input) → requests.post (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/classroom_note_translator.py (reported line 266)May include surrounding context.

python
with audio_path.open("rb") as f:
        files = {"file": (audio_path.name, f)}
        resp = requests.post(SENSEAUDIO_ENDPOINT, headers=headers, data=data, files=files, timeout=300)
    if resp.status_code >= 400:
        raise RuntimeError(f"SenseAudio API error {resp.status_code}: {resp.text}")
    return resp.json()

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

When the openai-compatible summary provider is selected, transcript contents are sent to an external LLM endpoint without any built-in disclosure or consent prompt. Because transcripts may contain sensitive educational, personal, or copyrighted material, undisclosed onward transfer materially increases privacy and compliance risk.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
88% confidence
Finding

Unlike the fixed SenseAudio and Notion endpoints, this external destination is configurable via OPENAI_BASE_URL, so outbound transmission can be redirected to an arbitrary host. In combination with the Authorization header, this can leak API credentials and transcript data to attacker-controlled infrastructure.

Content

Scanner excerpt · scripts/classroom_note_translator.py (reported line 401)May include surrounding context.

python
def openai_chat(messages: List[Dict[str, str]], model: str) -> str:
    api_key = os.getenv("OPENAI_API_KEY")
    base_url = os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1")
    if not api_key:
        raise RuntimeError("Missing OPENAI_API_KEY for openai-compatible summarization")

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The summarization prompt explicitly instructs the model to produce Chinese Markdown notes, and the system/user messages are written only in Chinese. This forces a specific output language rather than offering the user a language choice or making the locale constraint an explicit opt-in.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/classroom_note_translator.py (reported line 479)May include surrounding context.

python
"children": children,
    }

    resp = requests.post(
        "https://api.notion.com/v1/pages",
        headers={
            "Authorization": f"Bearer {token}",

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/classroom_note_translator.py (reported line 479)May include surrounding context.

python
"children": children,
    }

    resp = requests.post(
        "https://api.notion.com/v1/pages",
        headers={
            "Authorization": f"Bearer {token}",

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/classroom_note_translator.py (reported line 480)May include surrounding context.

python
}

    resp = requests.post(
        "https://api.notion.com/v1/pages",
        headers={
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json",

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The skill can export notes into Notion and write into an Obsidian vault, but it does not clearly frame these as state-changing operations affecting external systems or local knowledge bases. Users may expect passive note generation and not realize the skill can modify existing workspaces or repositories.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The markdown instructs use of --export-obsidian with a vault path and folder, implying the skill will write Markdown files into the user's local note repository. The description does not explicitly warn that local files will be created or modified in that location.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.