Back to skill

Security audit

Ai Coach Batch Session Summary

Security checks for vulnerabilities and agentic risk

Overview

The skill is coherent as an ASR report generator, but it handles private recording transcripts in ways that can expose them through public reports and bundled live-looking test data.

Install only in an environment where users understand that recording transcripts may be fetched, summarized, written to local temporary files, and uploaded to an externally reachable report link. Before approval, require private or expiring authenticated report delivery, removal of live transcript test artifacts, redaction of identifiers and raw excerpts by default, validation of custom dimensions and upload destinations, and locally bundled or integrity-pinned report scripts.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/publish_asr_report.py:71
Finding
Public disclosure of sensitive recording excerpts through persistent report URLs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_asr_insight_html.py:278-286`, `scripts/publish_asr_report.py:16-18, 71-86, 105-111`, and `SKILL.md:442-456` **Vulnerability Type**: Sensitive information exposure through publicly accessible report delivery **Risk Level**: High ### Vulnerable Code ```python DEFAULT_UPLOAD_URL = "https://legion.tongfudun.com/version/upload" DEFAULT_BUCKET = "legionclaw" DEFAULT_DOWNLOAD_BASE = "https://chat-minio.tongfudun.com/legionclaw" ``` ```python body, boundary = _multipart_body( { "bucket": bkt, "objectName": obj, "contentType": HTML_CONTENT_TYPE, "contentDisposition": "inline", }, "file", html_path, ) req = Request( up, data=body, method="POST", headers={"Content-Type": f"multipart/form-data; boundary={boundary}"}, ) ``` ```python report_url = f"{base}/{obj}" return { "ok": True, "reportUrl": report_url, "objectName": obj, "bucket": bkt, } ``` The uploaded HTML includes excerpts taken directly from recording transcripts: ```python for tag, snippet, file_alias in stat["excerpts"]: alias_display = html.escape(file_alias) if file_alias else "" alias_html = f'<span class="record-name">[{alias_display}]</span> ' if alias_display else "" items.append( f'<li>{alias_html}<span class="tag">{html.escape(tag)}</span>' f"{html.escape(snippet)}</li>" ) ``` The Skill explicitly requires public-link delivery: ```markdown This Skill's generated HTML must be delivered to the user through a publicly accessible download link. https://chat-minio.tongfudun.com/legionclaw/{objectName} ``` ### Technical Analysis The report contains verbatim excerpts from private recordings and may include customer names, financial discussions, personal circumstances, and confidential business information. The publisher uploads this report to a shared bucket with `Content-Disposition: inline`, and then constructs a stabl ...[truncated 2054 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Store reports in a private, per-user or per-tenant bucket. 2. Return short-lived, server-generated signed URLs rather than constructing public URLs locally. 3. Bind download authorization to the requesting user and current session. 4. Generate object names with at least 128 bits of cryptographic randomness. 5. Configure explicit expiration and automatic deletion for generated reports. 6. Redact or pseudonymize names, account details, and other sensitive entities before upload. 7. Allow users to choose whether verbatim excerpts are included. 8. Obtain explicit confirmation before uploading recording-derived content to an externally reachable service. 9. Do not assume the upload endpoint and download origin have equivalent access policies; validate the server response. 10. Prevent referrer leakage with `Referrer-Policy: no-referrer` and serve reports from an isolated, credential-free origin. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate_asr_insight_html.py:61
Finding
Stored script injection through unvalidated custom dimension names<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_asr_insight_html.py:61-69, 353, 509-510` **Vulnerability Type**: Stored HTML and JavaScript injection **Risk Level**: High ### Vulnerable Code Custom dimensions are loaded directly from a caller-selected JSON file: ```python if args.dimensions: dimensions = json.loads(Path(args.dimensions).read_text(encoding="utf-8")) ``` Dimension names are subsequently included in chart data: ```python coverage_values = [d["coverage"] for d in dim_stats] dim_names = [d["name"] for d in dim_stats] charts = [ { "id": "volume-trend", "type": "line", "title": "录音样本量按日变化", "labels": sorted_days, "values": volume_values, "meta": {"undatedRecords": undated, "aggregated": None}, }, { "id": "dimension-radar", "type": "radar", "title": "各维度覆盖率全景", "labels": dim_names, "values": coverage_values, "meta": {}, }, ] ``` The data is serialized and inserted into an executable script block: ```python charts_json = json.dumps(analysis["charts"], ensure_ascii=False) ``` ```python tail, n = re.subn( r"var charts = \[.*?\];", f"var charts = {charts_json};", tail, count=1, flags=re.DOTALL, ) if n == 0: tail = tail.replace( "(function () {", f"(function () {{\n var charts = {charts_json};", 1, ) ``` ### Technical Analysis The custom dimensions file is accepted without validating that: - The root value is an array. - Each item is an object. - `name` is a safe, length-limited string. - `keywords` is a bounded array of safe strings. - Names and keywords exclude HTML-closing sequences. Although `json.dumps` produces valid JSON, it does not make that JSON safe for direct placement inside an HTML `<script>` element. In HTML parsing, the literal sequence `</script>` terminates the script element even when it appears inside a JavaScript string. An attacker w ...[truncated 1859 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate custom dimensions against a strict schema before analysis: - Require an array root. - Require exact `name` and `keywords` properties. - Require strings with conservative length limits. - Bound the number of dimensions and keywords. 2. Reject HTML tags, control characters, and dangerous sequences such as `</script`. 3. Escape JSON for HTML script context by replacing at least: - `<` with `\u003c` - `>` with `\u003e` - `&` with `\u0026` - U+2028 with `\u2028` - U+2029 with `\u2029` 4. Prefer placing chart data in a non-executable element such as: ```html <script id="chart-data" type="application/json">...</script> ``` The contents must still be HTML-safe before parsing with `JSON.parse`. 5. Alternatively, generate chart initialization without embedding untrusted strings in executable markup. 6. Add a restrictive Content Security Policy that blocks arbitrary inline scripts and unauthorized outbound connections. 7. Add regression tests using `</script>`, HTML event handlers, quotes, Unicode separators, and oversized custom dimension values. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/publish_asr_report.py:67
Finding
Environment-controlled upload destination enables silent report exfiltration<![CDATA[ ## Vulnerability Details **File Location**: `scripts/publish_asr_report.py:67-86` **Vulnerability Type**: Unrestricted sensitive-data destination **Risk Level**: Medium ### Vulnerable Code ```python up = (upload_url or os.environ.get("LEGION_UPLOAD_URL") or DEFAULT_UPLOAD_URL).strip() bkt = (bucket or os.environ.get("LEGION_UPLOAD_BUCKET") or DEFAULT_BUCKET).strip() base = (download_base or os.environ.get("LEGION_DOWNLOAD_BASE") or DEFAULT_DOWNLOAD_BASE).rstrip("/") body, boundary = _multipart_body( { "bucket": bkt, "objectName": obj, "contentType": HTML_CONTENT_TYPE, "contentDisposition": "inline", }, "file", html_path, ) req = Request( up, data=body, method="POST", headers={"Content-Type": f"multipart/form-data; boundary={boundary}"}, ) ``` The request is sent without destination validation: ```python with urlopen(req, timeout=120) as res: if res.status < 200 or res.status >= 300: return { "ok": False, "error": f"报告上传失败(HTTP {res.status})", "objectName": obj, } ``` ### Technical Analysis `LEGION_UPLOAD_URL` can replace the intended upload endpoint with an arbitrary URL. The code does not enforce: - An exact destination hostname. - HTTPS. - A permitted port or path. - Certificate pinning or an organizational trust boundary. - Redirect restrictions. - Agreement between the upload destination and returned download base. Because the multipart body contains the complete generated HTML report, control over the process environment is sufficient to redirect sensitive transcript-derived content to an attacker-controlled server. Environment-based configuration may be operationally useful, but allowing unrestricted destinations for sensitive data violates least privilege. A lower-trust launcher, CI configuration, wrapper, container configuration, or adjacent automation component can alter the destination without modifying the audited ...[truncated 1193 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the upload URL environment override if it is not strictly required. 2. If multiple environments must be supported, map a fixed environment identifier to a compiled allowlist of exact HTTPS endpoints. 3. Reject all non-HTTPS schemes, unexpected hosts, ports, paths, user-information components, fragments, and malformed URLs. 4. Disable automatic redirects or revalidate every redirect destination against the same allowlist. 5. Keep the download base coupled to the validated upload configuration rather than independently environment-controlled. 6. Run the publisher with a minimal, immutable environment. 7. Use outbound network policy or firewall rules so the process can contact only approved upload hosts. 8. Record the validated destination in security logs without logging report content. 9. Add tests confirming that attacker-controlled hosts, HTTP URLs, alternate ports, and redirect chains are rejected. ]]>

T08 · Insecure Dependencies

Warning
Location
asr_insight_template.html:75
Finding
Third-party CDN scripts execute in pages containing sensitive report data<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_asr_insight_html.py:32-33, 79-81` and `asr_insight_template.html:75` **Vulnerability Type**: Unpinned remote executable dependency **Risk Level**: Medium ### Vulnerable Code ```python CHART_JS_CDN_PRIMARY = "https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js" CHART_JS_CDN_FALLBACK = "https://cdn.bootcdn.net/ajax/libs/Chart.js/4.4.1/chart.umd.min.js" ``` ```python if CHART_JS_CDN_FALLBACK not in tail: tail = tail.replace( 'crossorigin="anonymous"></script>', f"crossorigin=\"anonymous\" onerror=\"this.onerror=null;this.src='{CHART_JS_CDN_FALLBACK}'\"></script>", 1, ) ``` The template executes either remote resource: ```html <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js" crossorigin="anonymous" onerror="this.onerror=null;this.src='https://cdn.bootcdn.net/ajax/libs/Chart.js/4.4.1/chart.umd.min.js'"></script> ``` ### Technical Analysis Opening a generated report causes the browser to fetch and execute JavaScript from an external CDN. If the primary request fails, another external CDN is automatically trusted. No Subresource Integrity hash is supplied. Consequently, the effective code executed by a report can change after the Skill package has been audited, without any local source change. Compromise of either CDN, its account, DNS resolution, TLS trust path, or upstream package delivery can introduce arbitrary JavaScript into every opened report. The remote script executes in the context of a page containing private transcript excerpts. The fallback broadens the supply-chain trust boundary instead of failing closed. Loading the external resources may also disclose client network metadata and potentially the report URL through service logs or referrer behavior. ### Attack Path 1. An attacker compromises or substitutes the Chart.js asset delivered by either configured CDN. 2. A victim opens a ...[truncated 855 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Bundle a reviewed Chart.js build locally and embed it into the generated HTML so reports do not retrieve executable code at viewing time. 2. If external hosting is unavoidable, use an immutable asset URL and a verified Subresource Integrity hash. 3. Apply `referrerpolicy="no-referrer"` to external resources. 4. Remove the automatic fallback or require the fallback to have its own verified integrity hash. 5. Serve reports from an isolated, credential-free origin. 6. Apply a restrictive Content Security Policy that permits scripts only from specifically approved sources and blocks unexpected outbound connections. 7. Maintain a documented dependency update and integrity-verification process. 8. Consider replacing interactive charts with static SVG or canvas output generated locally. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
test-output/asr_completed_live.json:1
Finding
Live recording transcripts and persistent identifiers are bundled in test artifacts<![CDATA[ ## Vulnerability Details **File Location**: `test-output/asr_completed_live.json:1` **Vulnerability Type**: Sensitive production-like data committed to distributable artifacts **Risk Level**: Medium ### Vulnerable Data The bundled JSON contains complete transcript and recording records with fields such as: ```json { "asrText": "Complete conversation transcript content...", "deviceId": "6BFD103E-AF3A-23AA-A0D7-17A7E496C63C", "fileHash": "6b416367d53e2cb76ac111e98b5a3589872bc61542a22343b2a97848af82aad6", "filePath": "QmfM5VzsvbKYj7Dii5d8sWLmSqhtuRX8esXJXS4W8ez9Qd", "id": "2052408015707090945", "orgId": "1684487565205", "userId": "b7a19493cabc42e290c3d6c8a6243a7c" } ``` The file also contains workflow identifiers, task identifiers, recording times, filenames, AI-generated summaries, and lengthy ASR transcript content. ### Technical Analysis The test artifact contains substantially more information than is necessary to test report generation. Its filename identifies it as live data, and the records include stable identifiers and detailed conversational content. Anyone who receives or installs the Skill package can read the file directly without invoking the recording API or passing an authorization check. This bypasses the access boundaries normally associated with obtaining recording records. Even if individual identifiers are not credentials, their combination enables user correlation, organizational reconnaissance, and association of private conversations with stable accounts or devices. Generated HTML files in the same test directory may duplicate portions of that sensitive content. ### Attack Path 1. An unauthorized party obtains the Skill package, repository archive, build artifact, or installed project directory. 2. The party opens `test-output/asr_completed_live.json`. 3. The party extracts transcript text, summaries, user IDs, organization IDs, device IDs, hashes, workflow IDs, and recording metadata. 4. The party correlate ...[truncated 850 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the live-data JSON and all generated reports derived from it from the repository and release package. 2. Rewrite repository history if the data has already been published or broadly distributed. 3. Replace live records with minimal synthetic fixtures containing fictional transcripts and randomized non-production identifiers. 4. Include only fields required by each test case. 5. Add `test-output/` and generated reports to version-control ignore and release-exclusion rules where appropriate. 6. Add automated privacy and secret scanning to commits and build artifacts. 7. Review access logs and distribution history to determine who may have received the data. 8. Rotate or invalidate exposed identifiers where operationally possible. 9. Establish a documented retention policy for temporary ASR responses and generated reports. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (29)

Tainted flow: 'req' from os.environ.get (line 81, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
headers={"Content-Type": f"multipart/form-data; boundary={boundary}"},
    )
    try:
        with urlopen(req, timeout=120) as res:
            if res.status < 200 or res.status >= 300:
                return {
                    "ok": False,
Confidence
93% confidence
Finding
The upload destination is derived from environment variables (LEGION_UPLOAD_URL / LEGION_DOWNLOAD_BASE) and then used in urlopen without any allowlist, scheme restriction, or host validation. If an attacker can influence the runtime environment, they can redirect the HTML report upload to an arbitrary endpoint, causing exfiltration of potentially sensitive ASR analysis content and enabling SSRF-like outbound requests from the agent environment.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The manifest frames the skill as batch recording analysis, but the implementation also retrieves backend data, derives identifiers from session context, uploads generated artifacts, and exposes them via a public link. This description-behavior mismatch is dangerous because it obscures data access and publication behavior from users and reviewers, increasing the chance of unintended data disclosure.

Hidden Instructions

High
Category
Prompt Injection
Content
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8" />
Confidence
60% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Hidden Instructions

High
Category
Prompt Injection
Content
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8" />
Confidence
60% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Hidden Instructions

High
Category
Prompt Injection
Content
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8" />
Confidence
60% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Hidden Instructions

High
Category
Prompt Injection
Content
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8" />
Confidence
60% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The document presents all user-facing instructions, examples, and labels exclusively in Chinese and does not indicate that users may choose another language or locale. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale constraint is explicitly documented and justified.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill exercises sensitive capabilities—reading session metadata, writing files, and making network requests—without declaring an explicit tool scope or permissions boundary. That makes the skill harder to audit and can allow broader-than-expected access if the runtime grants default tool availability.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill does not clearly warn users that it will fetch backend call/transcript data and publish a generated report via a public download link. Missing user-facing disclosure undermines informed consent and increases the risk that sensitive communications are analyzed and redistributed unexpectedly.

Context-Inappropriate Capability

Medium
Confidence
98% confidence
Finding
The skill instructs the agent to publish generated HTML reports through a public internet download link. Because the source data consists of call recordings and ASR transcripts, publishing report outputs to a publicly reachable URL creates a real risk of exposing sensitive business or personal conversation content beyond the intended audience.

External Transmission

Medium
Category
Data Exfiltration
Content
START_TIME=$(date -v-30d +%Y-%m-%d)   # Linux 默认: date -d '30 days ago' +%Y-%m-%d
# 示例:用户说近三天 → START_TIME=$(date -v-3d +%Y-%m-%d)  # Linux: date -d '3 days ago' +%Y-%m-%d

curl -sS -X POST "http://192.168.96.17:8900/api/recordings/asr-completed" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d "{\"userId\":\"${AGENTID}\",\"startTime\":\"${START_TIME}\",\"endTime\":\"${END_TIME}\"}" \
Confidence
94% confidence
Finding
The skill sends identifiers and time-bounded recording queries to an external backend service over HTTP to retrieve ASR-completed recordings. Even if this is part of intended functionality, it is still a real data-transmission risk because sensitive transcript data is pulled from a remote service and later processed for sharing, and the use of plain HTTP further weakens transport confidentiality.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The module docstring states the skill generates an HTML insight report in Chinese, and the rest of the script consistently emits Chinese titles, labels, summaries, and error messages. There is no user opt-in, language selection, or documented justification that this skill is restricted to a Chinese-only locale, which matches the language/locale policy violation category.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
This JSON file contains multiple natural-language summaries, labels, and coaching outputs written exclusively in Chinese, including fields such as "会议纪要", "综合评分", and Chinese classification labels. There is no indication in the file that the language choice is user-selectable or justified as a region-specific constraint, which matches the policy category for forced language/locale behavior.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The page displays transcript excerpts and a user identifier but provides no visible privacy, sensitivity, retention, or sharing warning. Because the content includes ASR-derived conversation snippets that may contain financial, personal, or customer information, presenting it without clear sensitivity cues makes accidental disclosure, oversharing, and mishandling more likely.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
The report footer exposes a persistent-looking userId alongside transcript-derived business content, even though the skill's purpose is batch analysis and summary generation rather than identity display. This unnecessarily links sensitive conversation analytics to an identifier, increasing privacy risk, correlation risk across reports, and the blast radius if the HTML is shared or accessed by unintended parties.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The report includes a persistent user identifier in the footer and embeds verbatim transcript excerpts that can contain sensitive personal, financial, or conversational data. For a skill described as batch analysis and summary reporting, exposing raw excerpts and identifiers exceeds data minimization expectations and increases privacy leakage risk if the HTML is shared, cached, or accessed by unauthorized parties.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
The generated HTML imports Chart.js from third-party CDNs at runtime, which creates an unnecessary outbound dependency for a local analysis report. If the CDN, network path, or injected script is compromised, anyone opening the report could execute attacker-controlled JavaScript in the viewing context, and the page contents include sensitive transcript-derived data worth exfiltrating.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The report page fetches Chart.js from public CDNs at view time, creating an unnecessary third-party dependency and outbound network request for a locally generated analysis report. This can leak access metadata such as IP address, user agent, referrer/context, and introduces supply-chain or availability risk if the CDN is compromised or blocked.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
Opening the HTML report silently triggers requests to third-party CDNs without informing the user. In a skill handling recording-transcription analysis, that behavior is more sensitive because merely viewing a report can disclose that a user is accessing potentially sensitive business communication analytics.

Ae2

Medium
Category
analysis-evasion
Confidence
90% confidence
Finding
Artifact content does not match its filename extension

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The report loads Chart.js from public CDNs at render time, creating an unnecessary third-party dependency for a transcript-analysis report. This exposes viewers' IPs, user agents, referrers, and access timing to external services, and it also introduces supply-chain risk if the CDN resource is tampered with or unavailable.

Natural-Language Policy Violations

Low
Confidence
87% confidence
Finding
The manifest description and trigger/confirmation phrases are entirely specified in Chinese, and the skill prescribes fixed Chinese confirmation wording and response patterns. There is no indication that the user may choose another language or that the Chinese-only behavior is a justified regional constraint.

Context-Inappropriate Capability

Low
Confidence
80% confidence
Finding
The manifest frames the skill as batch analysis of recording transcripts, but the documented implementation requires querying host session state via session_status and parsing session keys to obtain an agent ID. While this may support data retrieval, it is a separate capability involving access to conversation/session metadata that is not mentioned in the manifest.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
The document root sets `lang="zh-CN"`, which hard-codes a specific language/locale for the rendered skill output. Under the policy, forcing a language without offering user choice or documenting a justified regional constraint is a natural-language policy concern.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
The document declares `lang="zh-CN"`, and the visible content is entirely Chinese, which indicates a fixed language/locale experience. Under the policy rule, forcing a specific language without user opt-in or documented justification is a natural-language policy concern.

Static analysis

No suspicious patterns detected.