Back to skill

Security audit

sync with webdav

Security checks for vulnerabilities and agentic risk

Overview

This skill does what it says, but it can upload local archives and WebDAV credentials without enforcing HTTPS, so users should review it before installing.

Install only if you trust the WebDAV destination and will configure WEBDAV_SITE with HTTPS. Review source paths and exclude rules carefully before running or scheduling it, because anything included in the archive will be sent off-host. Avoid using it for sensitive backups until HTTPS enforcement or an explicit secure-transport check is added.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/webdav_sync.py:215
Finding

Cleartext WebDAV Transport Permits Credential and Archive Disclosure

Content
View full analysis

Vulnerability Details

File Location: scripts/webdav_sync.py, lines 119-166 and 215
Vulnerability Type: Missing enforcement of encrypted transport for sensitive WebDAV operations
Risk Level: High

Vulnerable Code

python
def ensure_remote_dir(base_url: str, remote_subdir: str, curl_bin: str, auth_config: Path) -> None:
    current = base_url.rstrip("/") + "/"
    pieces = [piece for piece in remote_subdir.strip("/").split("/") if piece]
    for piece in pieces:
        current = current + quote(piece) + "/"
        result = run_cmd([
            curl_bin,
            "--config",
            str(auth_config),
            "-sS",
            "-o",
            "/dev/null",
            "-w",
            "%{http_code}",
            "-X",
            "MKCOL",
            current,
            "--connect-timeout",
            "20",
            "--max-time",
            "60",
        ], check=False)
        try:
            status = int((result.stdout or "").strip() or "0")
        except ValueError:
            status = 0
        if status not in SUCCESS_MKCOL:
            raise RuntimeError("MKCOL failed for {} with HTTP {}".format(current, status))


def upload_file(base_url: str, remote_subdir: str, archive_name: str, archive_path: Path, curl_bin: str, auth_config: Path) -> int:
    remote_url = "{}/{}/{}".format(
        base_url.rstrip("/"),
        "/".join(quote(part) for part in remote_subdir.strip("/").split("/") if part),
        quote(archive_name),
    )
    result = run_cmd([
        curl_bin,
        "--config",
        str(auth_config),
        "-sS",
        "-o",
        "/dev/null",
        "-w",
        "%{http_code}",
        "-T",
        str(archive_path),
        remote_url,
        "--connect-timeout",
        "30",
        "--max-time",
        "1800",
    ], check=False)
    try:
        return int((result.stdout or "").strip() or "0")
    except ValueError:
        return 0

The endpoint is obtained without validatin ...[truncated 2310 chars]

Remediation
View remediation

Remediation Suggestions

  1. Parse WEBDAV_SITE with urllib.parse.urlparse before creating the archive or credential file.
  2. Require the scheme to be exactly https and reject URLs with missing or unsupported schemes.
  3. Reject URLs containing embedded user information to avoid ambiguous credential handling.
  4. Keep TLS certificate verification enabled. Do not introduce curl options such as --insecure.
  5. Consider adding curl's --proto =https and --proto-redir =https controls to prevent protocol downgrade through redirects.
  6. Consider using --location-trusted only if strictly necessary; otherwise avoid forwarding credentials across redirects.
  7. If cleartext HTTP is indispensable for a tightly controlled local environment, place it behind an explicit opt-in flag, emit a prominent warning, and document that it must be protected by a trusted encrypted tunnel.

Example validation:

python
from urllib.parse import urlparse

parsed_url = urlparse(base_url)
if parsed_url.scheme.lower() != "https" or not parsed_url.netloc:
    raise ValueError("WEBDAV_SITE must be a valid HTTPS URL")
if parsed_url.username is not None or parsed_url.password is not None:
    raise ValueError("WEBDAV_SITE must not contain embedded credentials")

Curl should additionally be constrained to HTTPS:

python
[
    curl_bin,
    "--proto",
    "=https",
    "--proto-redir",
    "=https",
    "--config",
    str(auth_config),
    # remaining arguments
]
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (6)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/webdav_sync.py (reported line 202)May include surrounding context.

python
parser.add_argument("--source", action="append", required=True, help="Local file or directory to archive. Repeatable.")
    parser.add_argument("--archive-prefix", default="backup", help="Archive filename prefix.")
    parser.add_argument("--remote-subdir", required=True, help="Subdirectory under WEBDAV_SITE for uploaded archives.")
    parser.add_argument("--env-file", default=".env", help="Env file containing WebDAV credentials.")
    parser.add_argument("--tmp-dir", default="/tmp/openclaw-webdav-sync", help="Temporary directory for archive creation.")
    parser.add_argument("--exclude", action="append", default=[], help="Archive exclude glob, matched against tar entry names.")
    parser.add_argument("--compression", choices=["gz", "none"], default="gz", help="Archive compression mode.")

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill declares operational behavior that clearly involves local file access, archive creation, shell/binary invocation, and network upload, but it does not declare any tool scope or permissions boundary. That omission increases the chance an agent or operator will invoke the skill without an explicit understanding of its access requirements, which is especially risky because the skill handles local data and exfiltrates archives to a remote WebDAV endpoint.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The overview tells the agent to run a sync script but does not clearly warn that the workflow reads WebDAV credentials from environment variables and uploads local archives to a remote service. In this context, the missing disclosure matters because the skill's primary function is packaging local files and sending them off-host, so users may not realize sensitive data and secrets are involved before execution.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/webdav_sync.py (reported line 61)May include surrounding context.

python
"text": True,
        "check": False,
    }
    result = subprocess.run(cmd, **kwargs)
    if check and result.returncode != 0:
        stdout = result.stdout or ""
        stderr = result.stderr or ""

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The success notification message is hard-coded in Chinese, which imposes a specific language on users regardless of their preferences. The policy allows locale-specific behavior only when it is user-selectable or clearly justified, neither of which is present here.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The failure notification string is hard-coded in Chinese and does not offer any language choice or opt-in. This creates a natural-language locale policy issue because users may receive messages in an unexpected language.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.