Back to skill

Security audit

Caldav

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real CalDAV/Radicale management skill, but it needs review because it handles credentials and calendar data and includes privileged server-administration steps without enough safeguards.

Install only if you are comfortable giving an agent access to your CalDAV account and, for Radicale workflows, local server administration. Prefer HTTPS except for loopback testing, avoid storing passwords in plaintext config, do not paste real passwords into curl commands, review delete/update commands before running them, and treat systemctl enable/start or htpasswd user changes as explicit administrator actions.

Vulnerability Patterns
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • 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 (5)

T06 · System Persistence

Error
Location
SKILL.md:263
Finding
Privileged Radicale Service Persistence<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:263-268` **Vulnerability Type**: T06: System Persistence **Risk Level**: High ### Vulnerable Code ```bash ### Running as Service (systemd) # Enable and start sudo systemctl enable radicale sudo systemctl start radicale ``` ### Technical Analysis The documented `systemctl enable radicale` command modifies system-wide startup configuration so that Radicale starts automatically after subsequent reboots. The command requires elevated privileges and creates state that persists beyond the current Skill invocation. Running a Radicale server is related to the declared server-administration functionality. However, persistent automatic startup is not required for calendar, event, or todo management, nor is it required to run Radicale temporarily. The instruction therefore exceeds the minimum privileges and persistence necessary for most declared operations. The adjacent `systemctl start` command also launches a network-facing service with root-mediated service management. Actual exposure depends on Radicale's bind address, authentication, rights, and TLS configuration. ### Attack Path 1. A user or Agent follows the server setup instructions. 2. The user grants `sudo` authorization to execute `systemctl enable radicale`. 3. Systemd registers Radicale for automatic startup. 4. Radicale continues to start after reboots, independently of the Skill session. 5. If Radicale is configured with weak authentication, insecure rights, plaintext transport, or a public bind address, a remote party may access or attack the persistent service. ### Impact Assessment The command obtains system-level service-management privileges and changes persistent host configuration. It does not itself install a backdoor or demonstrate unauthorized code execution, but it expands the duration and availability of the Radicale attack surface. Potentially affected assets include all calendar and contact data available to the config ...[truncated 116 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not recommend `systemctl enable` as part of the default workflow. - Prefer running Radicale as an unprivileged foreground process for temporary or development use. - Require explicit, informed user approval before making a service persistent. - Clearly explain that enabling the service changes system-wide startup behavior and survives reboots. - Separate ordinary CalDAV client operations from privileged server-administration instructions. - If persistent deployment is requested, harden the systemd unit with a dedicated unprivileged account and options such as `NoNewPrivileges=yes`, `ProtectSystem=strict`, `PrivateTmp=yes`, and narrowly scoped writable paths. - Require authentication, restrictive rights, and TLS before exposing the service outside loopback. - Document rollback commands: ```bash sudo systemctl disable --now radicale ``` ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:34
Finding
Unpinned CalDAV and iCalendar Dependencies<![CDATA[ ## Vulnerability Details **File Locations**: `SKILL.md:34-39`; `scripts/calendars.py:240-261` **Vulnerability Type**: T08: Insecure Dependencies **Risk Level**: Medium ### Vulnerable Code ```bash ### Python Library Install the caldav library: pip install caldav For async support: pip install caldav[async] ``` The calendar import path also loads an additional dependency that is not declared or pinned: ```python try: # Parse and import events from icalendar import Calendar cal = Calendar.from_ical(ics_content) imported = 0 for component in cal.walk(): if component.name == "VEVENT": # Create event from component ics_data = f"BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//OpenClaw Import//EN\nBEGIN:VEVENT\n" # Add properties for line in component.to_ical().decode().split("\n"): if line and not line.startswith("BEGIN:VCALENDAR") and not line.startswith("END:VCALENDAR"): ics_data += line + "\n" ics_data += "END:VEVENT\nEND:VCALENDAR" calendar.save_event(ics_data) imported += 1 ``` ### Technical Analysis The installation instructions resolve the latest available `caldav` release without a version constraint or integrity hash. The import feature also depends on `icalendar`, but that dependency is only identified dynamically at runtime and is not pinned. This makes the effective code installed by users mutable after the Skill has been reviewed. A future compromised release, account takeover, dependency substitution, or unexpected incompatible update could introduce arbitrary installation-time or runtime behavior. No evidence was found that the currently named packages are malicious. The vulnerability is the absence of reproducible dependency constraints and integrity verification. ### Attack Path 1. A user follows the documented `pip install caldav` or `pip install caldav[async]` command, or installs the r ...[truncated 975 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin reviewed versions of all direct dependencies, including `caldav` and `icalendar`. - Use a lock file and cryptographic hashes, such as a hash-locked requirements file. - Declare `icalendar` explicitly rather than relying on a runtime installation hint. - Review and pin transitive dependencies where feasible. - Install dependencies in a dedicated virtual environment rather than globally. - Avoid `sudo pip install`. - Use an approved internal package mirror or other trusted index configuration for controlled deployments. - Add automated dependency vulnerability and provenance checks to the release process. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/utils.py:18
Finding
CalDAV Credentials Can Be Sent over Plaintext HTTP<![CDATA[ ## Vulnerability Details **File Locations**: `scripts/utils.py:18-59`; `SKILL.md:43-58` **Vulnerability Type**: T09: Insecure Skill Coding Practices **Risk Level**: Medium ### Vulnerable Code ```python def load_config() -> Dict[str, Any]: """Load configuration from file or environment.""" config = {} # Try config files for config_path in CONFIG_PATHS: if config_path.exists(): with open(config_path) as f: config.update(json.load(f)) break # Environment overrides if os.environ.get("CALDAV_URL"): config["url"] = os.environ["CALDAV_URL"] if os.environ.get("CALDAV_USER"): config["username"] = os.environ["CALDAV_USER"] if os.environ.get("CALDAV_PASSWORD"): config["password"] = os.environ["CALDAV_PASSWORD"] return config def get_client(): """Get authenticated CalDAV client.""" try: from caldav import DAVClient except ImportError: print("Error: caldav library not installed.") print("Install with: pip install caldav") raise SystemExit(1) config = load_config() if not config.get("url"): print("Error: No CalDAV URL configured.") print("Set CALDAV_URL environment variable or create config.json") raise SystemExit(1) return DAVClient( url=config.get("url"), username=config.get("username"), password=config.get("password"), ) ``` The documentation normalizes an HTTP endpoint: ```bash export CALDAV_URL="http://localhost:5232" export CALDAV_USER="your_username" export CALDAV_PASSWORD="your_password" ``` ### Technical Analysis The client passes credentials to any user-configured URL without validating the scheme or warning when authentication is performed over plaintext HTTP. The documented example uses loopback, which reduces exposure when used exactly as written. However, the same code accepts a remote `http://` endpoint without additional co ...[truncated 1129 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Require `https://` for every non-loopback endpoint. - Parse URLs explicitly and reject plaintext HTTP unless the hostname is a verified loopback address. - If insecure HTTP must be supported for development, require an explicit option such as `--allow-insecure-http` and emit a prominent warning. - Ensure TLS certificate verification remains enabled. - Document secure reverse-proxy deployment for Radicale. - Avoid presenting plaintext HTTP as a general configuration template. - Add tests covering remote HTTP rejection, loopback behavior, HTTPS acceptance, and malformed URLs. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:50
Finding
Plaintext CalDAV Password Storage without Permission Requirements<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:50-59` **Vulnerability Type**: T09: Insecure Skill Coding Practices **Risk Level**: Medium ### Vulnerable Code ```markdown Or use a config file at `~/.config/caldav/config.json`: ```json { "url": "http://localhost:5232", "username": "your_username", "password": "your_password" } ``` ``` ### Technical Analysis The recommended configuration format stores the CalDAV password directly in a JSON file. The instructions do not require restrictive permissions, validate file ownership, or recommend an operating-system credential store. The implementation in `scripts/utils.py` subsequently reads this plaintext file without checking whether it is group-readable or world-readable. Consequently, security depends entirely on external directory and file permissions that the Skill neither establishes nor validates. ### Attack Path 1. A user creates `~/.config/caldav/config.json` according to the documentation. 2. The file is created with permissions affected by the user's environment and `umask`. 3. Another local user, process, backup collector, support-bundle utility, or accidentally published repository obtains the file. 4. The plaintext username and password are recovered. 5. The credentials are used to authenticate to the CalDAV server and access or modify calendar data. ### Impact Assessment Exposure compromises the configured CalDAV account. An attacker may read private event and todo information, create misleading entries, alter schedules, or delete data according to the account's server-side permissions. This issue does not by itself grant root privileges. Its scope is the CalDAV account and any other service where the same credentials were reused. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Prefer an operating-system credential store, keyring, or secret manager. - If file-based configuration remains supported, separate secrets from non-sensitive settings. - Require the configuration file to have mode `0600` and its parent directory to have mode `0700`. - Add runtime checks that reject or prominently warn about group-readable or world-readable credential files. - Verify that the file is owned by the invoking user and is not an unexpected symbolic link. - Document that the file must not be committed to source control or included in support bundles. - Consider interactive secret entry for one-time operations. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/radicale.py:143
Finding
Radicale Configuration Output Does Not Redact Secrets<![CDATA[ ## Vulnerability Details **File Location**: `scripts/radicale.py:143-153` **Vulnerability Type**: T09: Insecure Skill Coding Practices **Risk Level**: Medium ### Vulnerable Code ```python def cmd_config_show(args): """Show Radicale configuration.""" config_path = find_config() if not config_path: print_result(False, "No Radicale config file found") return config = parse_config(config_path) print_result(True, f"Config from {config_path}", {"config": config, "path": str(config_path)}) ``` ### Technical Analysis `cmd_config_show()` parses every Radicale configuration section and serializes the complete resulting dictionary to standard output. No redaction is applied based on key names or configuration sections. Radicale authentication and plugin configurations may contain LDAP bind passwords, OAuth secrets, tokens, proxy credentials, or other sensitive values. If such values are present, invoking `config show` exposes them to the terminal, Agent transcript, process output capture, CI logs, or downstream automation. The default example configuration shown in the Skill does not include such embedded secrets, so exploitation depends on the contents of the actual configuration file. ### Attack Path 1. A Radicale configuration contains an authentication password, token, client secret, or other sensitive setting. 2. A user, Agent, diagnostic workflow, or log collector invokes: ```bash python3 scripts/radicale.py config show ``` 3. `parse_config()` loads all sections and values. 4. `print_result()` serializes the entire configuration to stdout. 5. The output is stored in a transcript, log, terminal recording, or automation artifact accessible to an unauthorized party. 6. The disclosed secret is used against the corresponding authentication or integration service. ### Impact Assessment The scope depends on the secrets present in the Radicale configuration. Potential consequences include unauthorized access ...[truncated 330 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Redact values whose keys contain patterns such as `password`, `passwd`, `secret`, `token`, `credential`, `api_key`, `private_key`, or `bind_password`. - Redact sensitive authentication and plugin sections by default. - Display only security-relevant non-secret settings for routine diagnostics. - If complete output is operationally necessary, provide a clearly named `--show-secrets` option that requires explicit confirmation and refuses non-interactive use by default. - Warn users that configuration output may be captured in logs or Agent transcripts. - Add tests ensuring nested and case-variant secret keys are redacted. ]]>
Vulnerability Patterns
  • 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
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (37)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The description claims a broader skill: managing CalDAV calendars and events, with special support for Radicale server, including event CRUD/query and server administration/configuration. This code chunk is narrower. It handles calendar listing, creation, deletion, inspection, and ICS import/export. While import/export touches events indirectly, there are no explicit event query, create, update, or delete commands, and no Radicale administration/configuration logic. Therefore the supplied code does not fully match the declared purpose and overstates implemented capabilities.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The description claims a broad skill for managing CalDAV calendars and events, plus administering a Radicale server. The supplied code only covers the Radicale administration portion: locating/parsing config files, checking systemd/process status, validating security/storage settings, managing htpasswd users, and verifying backend storage. There is no code to connect to a CalDAV server, enumerate calendars, or create/update/delete/query events. Because a major declared capability set (calendar and event management) is absent and the actual implementation is narrower and more server-admin oriented, this is a material description-behavior mismatch.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill advertises commands that can read environment variables, access files, write files, and execute shell commands, but it does not declare any explicit tool scope or allowed-tools boundary. In an agent setting, this weakens least-privilege controls and makes it easier for the skill to be invoked with broader capabilities than users or reviewers expect.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The examples place usernames and passwords directly in command lines and config snippets without warning that such secrets may be exposed through shell history, process listings, logs, or plaintext files. This encourages insecure credential handling and can lead to compromise of CalDAV accounts or server administration access.

Session Persistence

Medium
Category
Rogue Agent
Content
# List all calendars
python3 {baseDir}/scripts/calendars.py list

# Create a new calendar
python3 {baseDir}/scripts/calendars.py create --name "Work Calendar" --id work

# Delete a calendar
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.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill documents destructive actions such as deleting calendars, events, and todos without requiring confirmation or warning about irreversible effects. In an agent workflow, that increases the chance of accidental data loss from ambiguous prompts, mistaken identifiers, or automation errors.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
Administrative actions affecting Radicale users, configuration, and storage are presented without warnings about service disruption, authentication lockout, or system-wide consequences. In a skill that may be executed by an agent, missing safeguards around admin actions raises the risk of unintended outages or privilege-impacting changes.

External Transmission

Medium
Category
Data Exfiltration
Content
## Direct HTTP/DAV Operations

For low-level operations, use curl with CalDAV:

```bash
# Discover principal URL
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
```bash
# Enable and start
sudo systemctl enable radicale
sudo systemctl start radicale

# Check status
Confidence
82% confidence
Finding
The documentation instructs enabling and starting a system service with sudo, which requires elevated privileges and changes system state. In an agent context, normalizing privileged execution without strict gating can lead to unauthorized service modification or broader host impact if invoked carelessly.

Session Persistence

Medium
Category
Rogue Agent
Content
```bash
# Enable and start
sudo systemctl enable radicale
sudo systemctl start radicale

# Check status
Confidence
88% confidence
Finding
`systemctl enable` establishes persistence by configuring the service to start automatically on boot. Persistence is not inherently malicious for legitimate administration, but in an agent skill it is security-sensitive because it creates durable system changes that may outlast the immediate user task.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
```bash
# Enable and start
sudo systemctl enable radicale
sudo systemctl start radicale

# Check status
sudo systemctl status radicale
Confidence
82% confidence
Finding
Starting the Radicale service via sudo is a privileged operation that changes host state and may expose services on the network. Without approval and scoping controls, an agent could unintentionally start or alter infrastructure components beyond the user's intent.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
sudo systemctl start radicale

# Check status
sudo systemctl status radicale

# View logs
journalctl -u radicale -f
Confidence
70% confidence
Finding
Checking service status with sudo is a privileged-read operation and lower risk than modifying service state, but it still normalizes elevated execution. The main concern is overbroad privilege expectations within the skill rather than this command alone.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
return
        except EOFError:
            # Non-interactive mode
            print_result(False, "Use --force to skip confirmation")
            return

    try:
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
return
        except EOFError:
            # Non-interactive mode
            print_result(False, "Use --force to skip confirmation")
            return

    try:
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
return
        except EOFError:
            # Non-interactive mode
            print_result(False, "Use --force to skip confirmation")
            return

    try:
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The export command writes full calendar contents, which may include sensitive event details, to a user-specified path. Although the operation is intentional, there is no explicit warning in code comments, docstrings, or user-facing messaging that this action stores potentially sensitive data on disk.

Tainted flow: 'ics_content' from pathlib.Path.read_text (line 244, file read) → pathlib.Path.write_text (file write)

Medium
Category
Data Flow
Content
if args.output:
            output_path = Path(args.output)
            output_path.write_text(ics_content)
            print_result(True, f"Exported to {args.output}", {"events": len(events)})
        else:
            print(ics_content)
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.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The import command reads an ICS file and transmits each VEVENT to the remote calendar via save_event, which can modify server-side user data. The code lacks a confirmation prompt or explicit disclosure that local file contents will be uploaded and remote calendar state will be changed.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Check if service is running (systemd)
    try:
        result = subprocess.run(
            ["systemctl", "status", "radicale"],
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
This skill performs host-level process and service inspection (`systemctl`, `pgrep`) that goes beyond ordinary calendar/event management into local system administration. In a tool exposed to an agent, such capabilities can disclose host topology and running services, making the skill more dangerous if the agent can invoke it without strict scope and authorization.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Check if process is running (non-systemd)
    if data.get("service_status") != "running":
        try:
            result = subprocess.run(
                ["pgrep", "-f", "radicale"],
                capture_output=True,
                text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
`config show` returns the full parsed Radicale configuration, which can expose sensitive operational details such as certificate/key paths, authentication backend settings, storage locations, and other environment-specific metadata. In an agent context, even path disclosure can help an attacker map the host and target follow-on attacks.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
This code enables direct modification of the Radicale htpasswd authentication database by invoking an external command and writing to security-sensitive files. In agent-skill context, that is a powerful administrative primitive: if exposed improperly, an attacker could add accounts or alter authentication material, leading to unauthorized access to calendars or server administration.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Check if htpasswd command exists
    try:
        subprocess.run(["htpasswd", "--help"], capture_output=True, check=True)
    except (FileNotFoundError, subprocess.CalledProcessError):
        print_result(False, "htpasswd command not found. Install apache2-utils or httpd-tools")
        return
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
try:
        # Run htpasswd interactively (will prompt for password)
        result = subprocess.run(cmd)
        if result.returncode == 0:
            print_result(True, f"User '{args.username}' added to {users_path}")
        else:
Confidence
80% confidence
Finding
Although the command is executed without a shell, it launches an external host binary (`htpasswd`) chosen via PATH and performs a privileged user-management action against a filesystem path that may come from configuration or user input. In an agent skill context, this increases risk because the skill can modify authentication state on the host and may be abused to alter admin access if exposed without strong authorization boundaries.

Static analysis

No suspicious patterns detected.