Back to skill

Security audit

Dooray Hook

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a straightforward Dooray webhook sender, but it accepts arbitrary configured webhook destinations and allows disabling TLS verification, which can expose messages or webhook tokens if misconfigured.

Install only if you trust and control the OpenClaw config entry for this skill. Keep verify_ssl set to true, store only genuine Dooray webhook URLs, protect the config file permissions, and rotate any webhook token that may have been used with TLS verification disabled or placed in an untrusted config.

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

Warning
Location
scripts/send_dooray.py:104
Finding

Unrestricted Webhook Destination Enables Arbitrary Outbound Requests

Content
View full analysis

Vulnerability Details

File Location: scripts/send_dooray.py:104-130
Vulnerability Type: Missing destination validation / server-side request forgery
Risk Level: Medium

Vulnerable Code

python
webhook_url = rooms[room_name]
bot_name = dooray_config.get("botName", "OpenClaw")
bot_icon = dooray_config.get("botIconImage", "https://static.dooray.com/static_images/dooray-bot.png")

# Check for SSL verification override (Default: True)
verify_ssl = dooray_config.get("verify_ssl", True)

# Prepare payload
payload = {
    "botName": bot_name,
    "botIconImage": bot_icon,
    "text": message_text
}

payload_json = json.dumps(payload).encode('utf-8')

# Send POST request
try:
    req = urllib.request.Request(
        webhook_url,
        data=payload_json,
        headers={
            'Content-Type': 'application/json',
            'User-Agent': 'OpenClaw-Dooray-Skill/1.0'
        },
        method='POST'
    )

Technical Analysis

The script obtains the destination URL directly from the global OpenClaw configuration and passes it to urllib.request.Request without validating its scheme, hostname, port, path, or resolved address.

The declared functionality only requires access to Dooray incoming webhook URLs in the form https://hook.dooray.com/services/{TOKEN}. Accepting arbitrary destinations exceeds that minimum network scope. Although references/dooray-api.md recommends URL validation, the implementation does not enforce the documented format.

If an attacker can alter the configuration, or if a user is induced to add a malicious room definition, the process can be made to send a POST request to an attacker-controlled server or a network service reachable from the host. The transmitted JSON includes the caller-provided message, configured bot name, and configured icon URL.

Attack Path

  1. An attacker gains the ability to modify, influence, or sociall ...[truncated 1360 chars]
Remediation
View remediation

Remediation Suggestions

  1. Parse each configured URL with urllib.parse.urlsplit.
  2. Require the scheme to be exactly https.
  3. Require the normalized hostname to be exactly hook.dooray.com.
  4. Require the path to begin with /services/ and contain a nonempty token.
  5. Reject embedded user information, fragments, unexpected ports, malformed URLs, and control characters.
  6. Prevent redirects to destinations outside the same approved HTTPS origin, or disable automatic redirects for webhook requests.
  7. If deployment-specific Dooray domains must be supported, use an explicit administrator-controlled hostname allowlist rather than accepting arbitrary URLs.
  8. Consider rejecting loopback, link-local, private, and reserved resolved addresses as defense in depth.
  9. Validate all configured rooms at startup and fail closed before transmitting any message.
  10. Add tests covering external domains, HTTP URLs, user-information confusion, alternate ports, malformed paths, and cross-host redirects.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/send_dooray.py:138
Finding

Configurable TLS Verification Bypass Exposes Webhook Credentials and Messages

Content
View full analysis

Vulnerability Details

File Location: scripts/send_dooray.py:109, 138-147
Vulnerability Type: Improper certificate validation
Risk Level: Medium

Vulnerable Code

python
# Check for SSL verification override (Default: True)
verify_ssl = dooray_config.get("verify_ssl", True)
python
# [SECURITY] Conditional SSL Context
if verify_ssl:
    # Secure default: Validates SSL certificates
    ssl_context = ssl.create_default_context()
else:
    # Insecure opt-in: Ignores certificate errors (for proxies/self-signed certs)
    # This handles the [SSL: CERTIFICATE_VERIFY_FAILED] error if config allows it.
    ssl_context = ssl._create_unverified_context()

with urllib.request.urlopen(req, timeout=10, context=ssl_context) as response:

Technical Analysis

TLS certificate verification is enabled by default, but the verify_ssl configuration option can completely disable certificate and hostname authentication by selecting ssl._create_unverified_context().

When verification is disabled, TLS encryption does not authenticate the remote endpoint. An active network attacker, malicious proxy, or compromised network device can present an arbitrary certificate and impersonate the webhook server.

This is especially sensitive because the Dooray webhook token is embedded in the request URL and functions as the credential for posting to the associated room. Both that credential and the message payload become available to a successful interceptor.

The insecure option is documented in SKILL.md as a workaround for certificate failures. This increases the chance that users will disable verification persistently rather than install the appropriate trusted certificate authority.

Attack Path

  1. The user or administrator sets "verify_ssl": false, potentially to work around a corporate proxy or self-signed certificate.
  2. The Skill sends a message while connected through a hostile or compr ...[truncated 1146 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove support for ssl._create_unverified_context() and always verify server certificates and hostnames.
  2. Replace verify_ssl with an optional CA-bundle setting for private or corporate certificate authorities.
  3. Construct the context with ssl.create_default_context(cafile=approved_ca_path) when a custom CA is required.
  4. Validate that any configured CA file is an administrator-controlled regular file with restrictive permissions.
  5. Fail closed on certificate errors instead of recommending that users disable verification.
  6. Update SKILL.md to document installation of the corporate CA or configuration of an approved CA bundle.
  7. Rotate affected webhook tokens if messages have previously been transmitted with certificate verification disabled over an untrusted network.
  8. Add tests confirming that self-signed, expired, hostname-mismatched, and untrusted certificates are rejected.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (5)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
83% confidence
Finding

The skill documentation describes capabilities that read a local configuration file and send data over the network, but it does not declare any explicit tool scope such as permissions or allowed-tools. This creates an authorization gap: an agent may invoke file-read and outbound network actions without a clear least-privilege contract, increasing the risk of unintended secret access or exfiltration if the skill is misused or extended.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · SKILL.md (reported line 25)May include surrounding context.

To use this skill, you must define your Dooray! webhook URLs in the OpenClaw global config (~/.openclaw/openclaw.json).

Security Note: Webhook URLs are stored in your local config file. Ensure this file's permissions are restricted (e.g., chmod 600).

json
{

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/dooray-api.md (reported line 101)May include surrounding context.

Example Requests

cURL Example

bash
curl -X POST https://hook.dooray.com/services/YOUR_TOKEN \

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The text explicitly labels the official documentation as Korean, but does not offer an alternative language option or explain that the resource is region/language-specific. This can violate language/locale policy expectations when users are not given an opt-in or choice.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The module docstring presents the implementation scope as only using standard-library dependencies, which suggests a self-contained utility. In reality, the script depends on side-effectful external state in ~/.openclaw/openclaw.json, and the load_config docstring confirms that hidden dependency. This is an intent/documentation mismatch, not merely an omitted implementation detail, because the usage text does not disclose that configuration prerequisite.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.