T06 · System Persistence
- Location
SKILL.md:263- Finding
Privileged Radicale Service Persistence
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
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.
SKILL.md:263Privileged Radicale Service Persistence
SKILL.md:34Unpinned CalDAV and iCalendar Dependencies
scripts/utils.py:18CalDAV Credentials Can Be Sent over Plaintext HTTP
SKILL.md:50Plaintext CalDAV Password Storage without Permission Requirements
scripts/radicale.py:143Radicale Configuration Output Does Not Redact Secrets
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.
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.
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.
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.
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.
# 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
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.
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.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
For low-level operations, use curl with CalDAV:
# Discover principal URL
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.
# Enable and start
sudo systemctl enable radicale
sudo systemctl start radicale
# Check status
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.
# Enable and start
sudo systemctl enable radicale
sudo systemctl start radicale
# Check status
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.
# Enable and start
sudo systemctl enable radicale
sudo systemctl start radicale
# Check status
sudo systemctl status radicale
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.
sudo systemctl start radicale
# Check status
sudo systemctl status radicale
# View logs
journalctl -u radicale -f
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.
return
except EOFError:
# Non-interactive mode
print_result(False, "Use --force to skip confirmation")
return
try:
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.
return
except EOFError:
# Non-interactive mode
print_result(False, "Use --force to skip confirmation")
return
try:
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.
return
except EOFError:
# Non-interactive mode
print_result(False, "Use --force to skip confirmation")
return
try:
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.
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
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)
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 calls execute external commands. Without careful input validation, this enables command injection.
# Check if service is running (systemd)
try:
result = subprocess.run(
["systemctl", "status", "radicale"],
capture_output=True,
text=True,
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 calls execute external commands. Without careful input validation, this enables command injection.
# 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,
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.
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 calls execute external commands. Without careful input validation, this enables command injection.
# 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
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.
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:
No suspicious patterns detected.