Back to skill

Security audit

Smart Meeting Assistant

Security checks for vulnerabilities and agentic risk

Overview

This meeting assistant does what it claims, but it can send sensitive recordings, transcripts, and the API token to an arbitrary API base URL configured in the environment.

Review before installing. Use it only for meetings you are allowed to send to AstronClaw or an approved enterprise endpoint, avoid sensitive or regulated recordings unless your organization permits it, and do not set ASTRONCLAW_API_BASE to an untrusted URL. Prefer a hardened version that validates the API host and HTTPS, uses request timeouts, warns before upload, and documents retention expectations.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/meeting_assistant.py:30
Finding
Unrestricted API Base URL Can Expose Credentials and Confidential Meeting Data<![CDATA[ ## Vulnerability Details **File Location**: `scripts/meeting_assistant.py:30, 43-46, 75-81, 127-138, 178-189` **Vulnerability Type**: Unvalidated external service configuration and sensitive-data transmission **Risk Level**: High ### Vulnerable Code ```python DEFAULT_API_BASE = os.environ.get("ASTRONCLAW_API_BASE", "https://api.astronclaw.com") API_KEY = os.environ.get("ASTRONCLAW_API_KEY", "") ``` ```python def get_headers() -> dict: return { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json", } ``` ```python url = f"{DEFAULT_API_BASE}/v1/audio/transcriptions" files = {"file": (audio_path.name, audio_data)} data = {"model": "whisper-1", "language": language} headers = {"Authorization": f"Bearer {API_KEY}"} if API_KEY else {} response = requests.post(url, files=files, data=data, headers=headers) ``` ```python url = f"{DEFAULT_API_BASE}/v1/chat/completions" payload = { "model": "gpt-4o", "messages": [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_message}, ], "temperature": 0.3, } response = requests.post(url, json=payload, headers=get_headers()) ``` ```python url = f"{DEFAULT_API_BASE}/v1/chat/completions" payload = { "model": "gpt-4o", "messages": [ {"role": "system", "content": system_prompt}, {"role": "user", "content": f"Please extract todos from the following meeting content:\n\n{transcript}"}, ], "temperature": 0.2, "response_format": {"type": "json_object"}, } response = requests.post(url, json=payload, headers=get_headers()) ``` ### Technical Analysis The destination used for every API request is read directly from the `ASTRONCLAW_API_BASE` environment variable. The value is not validated before the application appends an API route and submits a request. The implementation does not enforce: - The HTTPS scheme. - The documented AstronClaw hostname. - An allowlist of trusted API hosts. - ...[truncated 2262 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse the configured URL with `urllib.parse.urlparse` before using it. 2. Require `https` and reject plaintext HTTP. 3. Allowlist the documented API hostname, or maintain a narrowly scoped list of explicitly approved enterprise endpoints. 4. Reject URLs containing embedded credentials, fragments, unexpected ports, or malformed hostnames. 5. Disable redirects or validate every redirect target before forwarding credentials or request bodies. 6. Never forward the bearer token to a different origin. 7. If custom endpoints are a required feature, require an explicit opt-in and display the exact destination before transmitting meeting data. 8. Add a request timeout and fail closed on TLS or destination-validation errors. 9. Document clearly that recordings and transcripts are transmitted to an external service. A hardened validation pattern could resemble: ```python from urllib.parse import urlparse ALLOWED_API_HOSTS = {"api.astronclaw.com"} def validate_api_base(value: str) -> str: parsed = urlparse(value) if parsed.scheme != "https": raise ValueError("The API base URL must use HTTPS") if parsed.hostname not in ALLOWED_API_HOSTS: raise ValueError("The API hostname is not approved") if parsed.username or parsed.password or parsed.fragment: raise ValueError("The API base URL contains prohibited components") return value.rstrip("/") ``` Requests should also use a finite timeout and either disable redirects or validate them explicitly: ```python response = requests.post( url, json=payload, headers=get_headers(), timeout=(5, 120), allow_redirects=False, ) ``` ]]>

T08 · Insecure Dependencies

Note
Location
scripts/meeting_assistant.py:7
Finding
Unpinned Third-Party Dependency Installation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/meeting_assistant.py:7-8, 23-27` **Vulnerability Type**: Unpinned dependency and non-reproducible installation guidance **Risk Level**: Low ### Vulnerable Code ```text pip install requests ``` ```python try: import requests except ImportError: sys.exit(1) ``` ### Technical Analysis The project instructs users to install `requests` without specifying a reviewed version, integrity hash, lock file, or approved package index. No dependency manifest is included elsewhere in the audited project. An unconstrained `pip install requests` resolves whichever compatible release the active package index serves at installation time. This makes installation non-reproducible and increases exposure to: - A future compromised or vulnerable package release. - A compromised or misconfigured Python package index. - Dependency substitution through an unsafe mirror. - Unexpected compatibility or security changes in transitive dependencies. The dependency name itself is correctly spelled, and the audit found no evidence that the project intentionally references a malicious package. The risk arises from the absence of version and artifact integrity controls. ### Attack Path 1. A user follows the documented instruction to run `pip install requests`. 2. The user's pip configuration uses a compromised, malicious, or otherwise untrusted package index or mirror, or resolves a future unsafe release. 3. Pip downloads an artifact without the project verifying a pinned version or cryptographic hash. 4. Package installation or subsequent import executes code from the unsafe artifact. 5. That code runs with the privileges of the user or environment performing the installation and can access data available to that context. ### Impact Assessment The exact impact depends on the privileges of the account performing installation. A malicious dependency could potentially: - Execute arbitrary code as the installing user. ...[truncated 471 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add a dependency manifest and pin `requests` to a reviewed version or narrowly controlled compatible range. 2. Generate and commit a lock file containing exact transitive dependency versions. 3. Include cryptographic hashes and install with pip's `--require-hashes` option. 4. Document the approved Python package index and avoid untrusted extra indexes or mirrors. 5. Use an isolated virtual environment rather than installing into a system interpreter. 6. Enable automated dependency vulnerability and update monitoring. 7. Review and regenerate the lock file when intentionally upgrading dependencies. For example, maintain a reviewed requirements file with hashes and install it using: ```bash python -m pip install --require-hashes -r requirements.txt ``` The committed requirements file should contain the exact reviewed versions and hashes for `requests` and its resolved transitive dependencies. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (12)

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

Critical
Category
Data Flow
Content
data = {"model": "whisper-1", "language": language}
    headers = {"Authorization": f"Bearer {API_KEY}"} if API_KEY else {}
    
    response = requests.post(url, files=files, data=data, headers=headers)
    
    if response.status_code != 200:
        raise Exception(f"转写失败: {response.status_code} - {response.text}")
Confidence
95% confidence
Finding
The request destination is derived from ASTRONCLAW_API_BASE in the environment, so an attacker or misconfigured runtime can redirect audio uploads to an arbitrary host. Because this call sends raw meeting audio and may include a bearer token, the issue can cause silent exfiltration of sensitive meeting content and credentials to an attacker-controlled endpoint.

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

Critical
Category
Data Flow
Content
"temperature": 0.3,
    }
    
    response = requests.post(url, json=payload, headers=get_headers())
    
    if response.status_code != 200:
        raise Exception(f"生成纪要失败: {response.status_code} - {response.text}")
Confidence
95% confidence
Finding
The chat completion URL is also built from the environment-controlled API base, allowing transcript content and the Authorization header to be sent to an attacker-selected server. In this skill, transcripts may contain confidential business discussions, so endpoint redirection directly enables sensitive data exfiltration.

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

Critical
Category
Data Flow
Content
"response_format": {"type": "json_object"},
    }
    
    response = requests.post(url, json=payload, headers=get_headers())
    
    if response.status_code != 200:
        raise Exception(f"提取待办失败: {response.status_code} - {response.text}")
Confidence
95% confidence
Finding
This request sends extracted meeting content to a URL derived from environment input, so a malicious or compromised environment can redirect structured transcript-derived data and API credentials externally. The structured JSON output may make exfiltrated action items and ownership data especially useful to an attacker.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill documentation describes use of environment variables, local file I/O, and outbound API access, but it does not declare any explicit tool scope or permissions boundaries. This increases the chance of over-privileged execution or silent access to sensitive files/network resources beyond what users expect, especially in agent environments that rely on manifest-declared scopes for enforcement.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger phrases include broad terms like '转写', '会议录音', 'meeting minutes', and 'transcribe', which are generic enough to match unrelated user requests. Overbroad invocation can cause the skill to activate unexpectedly and process local files or send content to an external API without the user intending to use this specific workflow.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill handles meeting recordings and transcripts, which commonly contain sensitive business or personal information, yet the description does not warn users that this data may be transmitted to an external API and stored in generated files. That omission undermines informed consent and can lead to accidental disclosure of confidential meeting content, participant identities, and action items.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill uploads meeting audio and later transcript content to external services, but the CLI does not provide a clear user-facing consent or privacy warning at the time of transmission. Since meeting recordings commonly contain confidential or regulated information, users may unknowingly disclose sensitive data to third-party infrastructure.

External Transmission

Medium
Category
Data Exfiltration
Content
"temperature": 0.3,
    }
    
    response = requests.post(url, json=payload, headers=get_headers())
    
    if response.status_code != 200:
        raise Exception(f"生成纪要失败: {response.status_code} - {response.text}")
Confidence
84% confidence
Finding
This code transmits full meeting transcripts to an external LLM API for summarization. In the context of a meeting assistant, the transmission is functional and expected, but it is still security-relevant because highly sensitive internal discussions may leave the local environment and be processed by a third party.

External Transmission

Medium
Category
Data Exfiltration
Content
"response_format": {"type": "json_object"},
    }
    
    response = requests.post(url, json=payload, headers=get_headers())
    
    if response.status_code != 200:
        raise Exception(f"提取待办失败: {response.status_code} - {response.text}")
Confidence
84% confidence
Finding
This call sends transcript content to an external API to extract action items. Although aligned with the skill's purpose, it exposes potentially sensitive business information, names, deadlines, and decisions to a remote service, which is a real data-handling risk in security-sensitive environments.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
The transcription function defaults to `language="zh"`, and the prompts/help text are written in Chinese, causing the tool to assume a specific language unless the user overrides it. The file does not indicate that this locale restriction is optional by default or justified as a region-specific tool.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The command-line interface sets `--language` default to `zh` for transcription-related commands, which enforces a locale assumption unless changed manually. This is a natural-language policy concern because users are not first offered a language choice or automatic detection.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The full-pipeline CLI path also defaults `--language` to `zh`, reinforcing a fixed locale assumption across the tool. There is no nearby justification that the skill is intentionally limited to Chinese-language use cases.

Static analysis

No suspicious patterns detected.