Back to skill

Security audit

synology-calendar

Security checks for vulnerabilities and agentic risk

Overview

This Synology Calendar skill is mostly purpose-aligned, but it defaults to sending account credentials over plain HTTP and lacks adequate warnings around secrets and deletion actions.

Review this before installing. Use only an HTTPS Synology URL, prefer a dedicated low-privilege account, avoid passing the password on the command line, and be careful with delete commands because the skill can modify or remove calendar data.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
client.py:14
Finding
Credentials and Session Tokens May Be Transmitted over Plaintext HTTP<![CDATA[ ## Vulnerability Details **File Location**: `client.py:14`, `client.py:32-36`, `client.py:68-75`; `SKILL.md:18-23` **Vulnerability Type**: Plaintext transmission of credentials and session tokens **Risk Level**: High ### Vulnerable Code `client.py:14`: ```python URL = os.environ.get("SYNOLOGY_URL", "http://{nas_ip}:5000") ``` `client.py:32-36`: ```python data = { "account": self.user, "passwd": self.password, "format": "sid" # 必须设为 "sid" } ``` `client.py:68-75`: ```python url = f"{self.url}{endpoint}" if "?" in url: url += f"&_sid={self.sid}" else: url += f"?_sid={self.sid}" # JSON body 中不需要包含 did/sid r = self.s.request(method, url, **kwargs) ``` `SKILL.md:18-23`: ```bash export SYNOLOGY_URL="http://{nas_ip}:5000" # 内网地址 export SYNOLOGY_USER="{username}" export SYNOLOGY_PASSWORD="your-password" ``` ### Technical Analysis The default configuration and documented example use unencrypted HTTP. The login method transmits the Synology username and password in a JSON request body. After authentication, `_request()` appends the SID authentication token to the request URL. When HTTP is used, transport-layer encryption and server authentication are absent. Any party able to observe or manipulate traffic between the client and NAS can read the account password, SID, request bodies, and returned calendar information. Placing the SID in the query string creates additional exposure even when HTTPS is enabled. Complete URLs can be recorded by reverse proxies, HTTP access logs, monitoring systems, debugging middleware, or network appliances. This behavior is documented as an API requirement, but the implementation does not compensate by requiring encrypted transport or warning users against insecure endpoints. ### Attack Path 1. A user follows the documented configuration and sets `SYNOLOGY_URL` to an `http://` NAS address. 2. The client submits the username and password to the login endpoint over plaintext HTTP. 3. An attacker w ...[truncated 1338 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Change the default URL and all documentation examples to HTTPS: ```python URL = os.environ.get("SYNOLOGY_URL", "https://{nas_ip}:5001") ``` 2. Reject plaintext HTTP by default before any credentials are sent: ```python from urllib.parse import urlparse parsed = urlparse(self.url) if parsed.scheme != "https": raise ValueError("SYNOLOGY_URL must use HTTPS") ``` 3. If plaintext HTTP is indispensable for isolated development, require an explicit opt-in such as `SYNOLOGY_ALLOW_INSECURE_HTTP=true` and display a prominent warning. It should never be enabled by default. 4. Keep TLS certificate verification enabled. Do not introduce `verify=False`. For private NAS certificates, support a user-provided CA bundle or document how to install the NAS CA certificate. 5. If supported by the Synology API, transmit the SID through an authorization header or secure session cookie instead of the query string. 6. If the API strictly requires `_sid` in the URL, ensure HTTPS is mandatory and configure reverse proxies, access logs, exception handlers, and telemetry systems to redact the `_sid` parameter. 7. Invalidate the local SID after logout and rotate any credentials or sessions that may previously have traversed an untrusted HTTP connection. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
client.py:265
Finding
CLI Password Argument and Authentication Identifier Logging Expose Sensitive Values<![CDATA[ ## Vulnerability Details **File Location**: `client.py:40`, `client.py:265-268`, `client.py:301` **Vulnerability Type**: Sensitive information exposure through process arguments and console output **Risk Level**: Medium ### Vulnerable Code `client.py:40`: ```python print(f"✓ 登录成功 (did: {self.did[:10]}...)") ``` `client.py:265-268`: ```python parser = argparse.ArgumentParser(description="Synology Calendar API (新版)") parser.add_argument("--url", default=URL, help="NAS URL") parser.add_argument("--user", default=USER, help="用户名") parser.add_argument("--password", default=PWD, help="密码") ``` `client.py:301`: ```python cal = SynologyCalendar(args.url, args.user, args.password) ``` ### Technical Analysis The CLI accepts a plaintext password through `--password`. Command-line arguments commonly remain visible in shell history and may be exposed to process-listing tools, process-monitoring agents, CI/CD logs, terminal recordings, crash reports, or audit systems. The login method also prints the first ten characters of `did`, an authentication-related device identifier returned alongside the SID. Although the displayed prefix is not demonstrated to be independently sufficient for authentication, logging authentication metadata is unnecessary and increases information exposure. It can also help correlate sessions, users, devices, and collected logs. Environment-variable use is already supported and is preferable to a command-line argument, although environment variables must also be protected from diagnostic output and unsafe process inspection. ### Attack Path 1. A user invokes the client with a command such as `python client.py --password secret list-calendars`. 2. The complete command is retained in shell history or captured by process monitoring, CI logs, terminal telemetry, or another local observation mechanism. 3. A local user or operator with access to those records obtains the password. 4. The attacker uses the recovered credentials to auth ...[truncated 991 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the `--password` argument so secrets cannot be supplied in process arguments. 2. Prefer a protected environment variable for noninteractive execution, or prompt interactively with `getpass`: ```python from getpass import getpass password = os.environ.get("SYNOLOGY_PASSWORD") if not password: password = getpass("Synology password: ") ``` 3. Ensure CI and automation systems inject the password through a secret manager and mask it in logs. Avoid placing credentials in command files, scripts, or shell history. 4. Replace the login message with a generic success message that contains no returned authentication identifiers: ```python print("Login successful") ``` 5. Do not print SID, DID, passwords, complete request URLs, or authentication responses in normal or debug output. 6. Review existing shell histories, CI logs, and terminal recordings for exposed passwords. Rotate any credential that may have been recorded and terminate associated active sessions. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (12)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The code substantially matches the declared Synology Calendar management purpose for calendars, events, and todos: it logs in, performs event CRUD-related operations (create/get/list/delete), task creation/listing, and calendar/timezone listing against Synology Calendar API endpoints. However, the description explicitly says it supports contacts, and there is no contact functionality in the supplied code chunk. This is a description-behavior mismatch due to an overstated supported capability. No unrelated or suspicious undeclared behaviors are present beyond standard authentication and HTTP session handling.

Missing User Warnings

High
Confidence
99% confidence
Finding
The default server URL uses plain HTTP, and the login flow sends username and password directly to that endpoint. If used on an untrusted or even moderately exposed network, credentials and session tokens can be intercepted or modified by a man-in-the-middle, leading to account compromise and unauthorized calendar access.

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill documents use of environment variables and network access to authenticate to and manipulate a Synology Calendar instance, but it declares no explicit tool scope or permissions. In an agent setting, missing scope boundaries can allow broader-than-expected access to secrets and outbound connections, reducing reviewability and increasing the chance of unauthorized or surprising behavior.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The skill instructs users to place a username and password in environment variables but provides no warning about secret handling, exposure risk, shell history, process inspection, or least-privilege account usage. Because the skill authenticates to a NAS service over the network, poor credential hygiene could lead to compromise of calendar data or broader Synology account access.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The documentation advertises delete operations for calendars and events without any warning, confirmation guidance, or mention of irreversible data loss. In an agent or automation context, this increases the likelihood of accidental destructive actions against user calendar data, especially if invoked from ambiguous prompts or scripts.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The manifest requires a username and password for a Synology account but does not provide any user-facing warning or guidance about handling these credentials as sensitive secrets. This increases the risk that users supply long-lived admin credentials without understanding storage, logging, or least-privilege implications, which could lead to account compromise or broader NAS exposure if the skill or surrounding platform mishandles them.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
The code forces `Asia/Shanghai` as the timezone when creating non-all-day events, which imposes a locale-specific behavior without user opt-in. This can violate language/locale policy expectations because users in other regions are not given a choice or informed override path in the natural-language interface.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The `delete_event` method issues a DELETE request that removes calendar events, and the CLI directly invokes it from the `delete-event` command. The code prints after deletion succeeds or fails, but there is no prior confirmation prompt, cautionary comment/docstring, or user-facing warning about the destructive action.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
For timed tasks, the code assigns `Asia/Shanghai` as the timezone automatically, which enforces a region-specific locale setting. The file does not present this as an opt-in choice or clearly justify the restriction as region-specific behavior.

Natural-Language Policy Violations

Medium
Confidence
84% confidence
Finding
When `due` is passed as a non-string integer, the task code sets `tz_id` to `Asia/Shanghai`, again enforcing a locale-specific default. Because users are not offered a timezone choice, this is a natural-language policy concern around locale handling.

Natural-Language Policy Violations

Low
Confidence
77% confidence
Finding
The documentation presents `Asia/Shanghai` as the timezone example for event creation and uses it directly in the sample call, without noting that users should choose a timezone appropriate to their locale. This can be interpreted as a locale-specific default embedded in the skill instructions without opt-in.

Description-Behavior Mismatch

Low
Confidence
98% confidence
Finding
The manifest description states the skill supports calendars, events, todos, and contacts. In this file, the implemented API methods cover login/logout, event management, task management, calendar listing, and timezone listing, but there are no contact endpoints, methods, or CLI commands.

Static analysis

Detected: suspicious.env_credential_access, suspicious.install_untrusted_source

Python code POSTs credential environment variables to an environment-controlled URL.

Critical
Code
suspicious.env_credential_access
Location
client.py:41

Install source points to URL shortener or raw IP.

Warn
Code
suspicious.install_untrusted_source
Location
_meta.json:12