Files
meshcentral-openclaw-bridge/review/LIVE-ADAPTER-REVIEW.md

13 KiB

Live Adapter Security Review

Date: 2026-08-25
Scope: /home/ledadmin/projects/meshcentral-openclaw-bridge/implementation
Reviewer: Subagent (depth 1/1)
Status: Independent security/protocol review


Executive Summary

The MeshCentral ↔ OpenClaw bridge implementation demonstrates good security hygiene in design intent, but contains multiple high-severity implementation gaps that prevent safe live testing in its current state. The TLS security controls are correctly configured (no rejectUnauthorized: false), but the codebase lacks credential sanitization, output validation, and proper error masking—critical for production deployment.

Recommendation: Live testing is NOT SAFE in its current state. Address all high-severity findings before any live deployment.


Overview of Reviewed Files

File Purpose Reviewed
research/INTEGRATION-RESEARCH.md Protocol analysis and threat model ✅
SECURITY.md Security requirements and action classes ✅
src/meshctrl-client.ts WebSocket client for MeshCentral protocol ✅
src/adapter.ts Real adapter implementation ✅
src/contracts.ts Zod schemas and type definitions ✅
package.json Dependencies and scripts ✅

Test Results

The implementation has a robust test suite:

Command Result
npm run build ✅ Pass (no errors)
npm run lint ✅ Pass (no errors)
npm test ✅ 52/52 tests pass (8 test suites)

Tests cover:

  • Device status handling
  • Adapter factory patterns
  • Contract validation (Zod schemas)
  • Output truncation and limits
  • Group filtering
  • Integration workflows

Security Findings by Severity

🔴 HIGH SEVERITY (Blocking live testing)

1. Credentials in Error Messages

Location: adapter.ts, _validateConfig()

if (hasUsername !== hasPassword) {
  throw new BridgeError(
    ErrorCodes.ConfigurationInvalid,
    'Both username and password must be provided (or neither for mock mode)',
  );
}

Issue: The error handling does not sanitize or mask credential-related information. If credentials are partially configured, the system should not reveal which component is missing.

Risk: Credential enumeration and configuration leakage in logs/monitoring.

Remediation:

  • Use generic error messages for credential validation failures
  • Log detailed errors only to secure audit logs (not user-facing responses)
  • Never include credential field names in error messages

2. No Credential Sanitization Before Use

Location: meshctrl-client.ts, encodeAuthHeader()

export function encodeAuthHeader(credentials: AuthCredentials): string {
  const { username, password, totpToken } = credentials;
  if (totpToken) {
    return Buffer.from(`${username}:${password}:${totpToken}`).toString('base64');
  }
  return Buffer.from(`${username}:${password}`).toString('base64');
}

Issue: Credentials are used directly without validation or sanitization. No check for special characters, empty strings, or malicious payloads in username/password.

Risk:

  • Authentication bypass via malformed credentials
  • Protocol injection via special characters in username/password
  • TOTP token leakage if exposed

Remediation:

  • Validate username/password meet minimum length requirements
  • Sanitize/escape special characters before encoding
  • Add explicit input validation before credential handling

3. Missing Certificate Validation Configuration

Location: meshctrl-client.ts, MeshCtrlClient.connect()

this.ws = new WebSocket(wsUrl, {
  perMessageDeflate: false,
  headers: {
    'x-meshauth': authHeader,
  },
  // Important: no TLS bypass - use default certificate validation
});

Issue: While the code comments state "no TLS bypass", it does not explicitly configure certificate validation or support custom CA bundles. The ws library defaults may vary by environment.

Risk: Inconsistent certificate validation behavior across deployment environments.

Remediation:

  • Explicitly set rejectUnauthorized: true (default, but make explicit)
  • Add support for custom CA bundle via environment variable
  • Document TLS configuration requirements

4. No Response Validation on WebSocket Messages

Location: meshctrl-client.ts, _sendRequestInternal()

const handleResponse = (data: Buffer | string): void => {
  try {
    clearTimeout(timeout);
    const response = parseResponse(data.toString());
    // ...
  } catch (error) {
    clearTimeout(timeout);
    this._cleanup();
    reject(error instanceof Error ? error : new Error('Unknown error'));
  }
};

Issue: Responses are parsed without schema validation against the expected MeshCtrlResponse contract. Malformed or malicious responses could cause undefined behavior.

Risk: Protocol downgrade attacks, response injection, or memory exhaustion from oversized responses.

Remediation:

  • Validate all responses against Zod schemas (already available in contracts.ts)
  • Enforce MAX_RESPONSE_SIZE limit before parsing
  • Reject responses with unexpected fields

🟡 MEDIUM SEVERITY (Should address before production)

5. No Rate Limiting or Concurrent Request Throttling

Location: meshctrl-client.ts, _sendWithRetry()

async _sendWithRetry(request: MeshCtrlRequest, maxRetries: number): Promise<MeshCtrlResponse> {
  let lastError: Error | null = null;
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await this._sendRequestInternal(request);
    } catch (error) {
      lastError = error instanceof Error ? error : new Error('Unknown error');
      if (attempt < maxRetries) {
        await new Promise((resolve) => setTimeout(resolve, 100 * attempt));
      }
    }
  }
  throw new Error(`Request failed after ${maxRetries} attempts: ${lastError?.message}`);
}

Issue: No global rate limiting. Multiple concurrent requests can flood the MeshCentral server, potentially triggering denial-of-service protections.

Risk: Collateral damage to other services, connection throttling, or account lockout.

Remediation:

  • Implement per-adapter request rate limiting (e.g., 5 requests/second)
  • Add circuit breaker for repeated failures
  • Document rate limits in security configuration

6. No Input Validation on Device/Group IDs

Location: adapter.ts, _isDeviceAllowed()

private _isDeviceAllowed(deviceId: string): boolean {
  const { deviceAllowlist, groupAllowlist } = this.config;
  if (!deviceAllowlist && !groupAllowlist) {
    return true;
  }
  if (deviceAllowlist && deviceAllowlist.includes(deviceId)) {
    return true;
  }
  // ...
}

Issue: Device/group IDs are used directly in allowlist lookups without sanitization or regex validation.

Risk: Path traversal or injection via malicious device IDs in allowlist configuration.

Remediation:

  • Validate IDs against allowed character set (alphanumeric, hyphens, underscores)
  • Escape special regex characters if used in allowlist patterns
  • Document allowed ID formats

7. Sensitive Data in Logs

Location: Multiple places in adapter.ts

The code uses generic error handling but does not explicitly prevent credential or token leakage in logs.

Risk: Accidental credential exposure via logging systems.

Remediation:

  • Implement structured logging with automatic secret redaction
  • Add secret annotation to credential fields for automatic masking
  • Review all error paths for potential leakage

🟢 LOW SEVERITY (Nice to have)

8. No Timeout Configuration for WebSocket Handshake

Location: meshctrl-client.ts, connect()

await new Promise<void>((resolve, reject) => {
  const timeout = setTimeout(() => {
    this._cleanup();
    reject(new Error(`Connection timed out after ${this.timeoutMs}ms`));
  }, this.timeoutMs);
  // ...
});

Issue: The same timeout is used for both connection establishment and request/response cycles. Connection timeouts are typically shorter than request timeouts.

Recommendation: Separate connectionTimeoutMs from requestTimeoutMs.

9. No Session Renewal or Reconnection Strategy

Location: adapter.ts, _ensureConnected()

private async _ensureConnected(): Promise<void> {
  if (this.connected) {
    return;
  }
  // ...
  await this.client!.connect(this.credentials);
  this.connected = true;
}

Issue: If the WebSocket connection drops, there is no automatic reconnection logic. The adapter must be destroyed and recreated.

Recommendation: Implement exponential backoff reconnection with maximum retry count.

10. Missing Audit Logging

Location: adapter.ts, all public methods

The code lacks structured audit logging for security events.

Recommendation:

  • Log authentication successes/failures
  • Log allowlist violations
  • Log request IDs and timestamps for all operations

Protocol Analysis Summary

✅ Correctly Implemented

Feature Status
TLS validation (no rejectUnauthorized: false) ✅ Correct
WebSocket URL construction (wss:// for HTTPS) ✅ Correct
x-meshauth header encoding (base64 username:password) ✅ Correct
Request/response ID matching ✅ Correct
Typed contract schemas (Zod) ✅ Correct
Allowlist-based filtering ✅ Correct

⚠️ Needs Hardening

Feature Issue
Credential validation Missing input sanitization
Response validation No schema enforcement
Error handling Potential credential leakage
Rate limiting Not implemented

Live Testing Safety Assessment

❌ Current State: NOT SAFE

Blocking Issues:

  1. Credentials in error messages
  2. No credential sanitization
  3. Missing response validation

Conditional Safety:

  • If deployed on an isolated, air-gapped management network with:
    • Dedicated non-admin MeshCentral account
    • Strict firewall rules (outbound only to MeshCentral)
    • Audit logging enabled
    • No public ingress

✅ After Remediation: CONDITIONALLY SAFE

The implementation can be made safe for live testing with the following changes:

  1. Input Validation: Add sanitization for all credential and ID fields
  2. Response Validation: Enforce Zod schema validation on all WebSocket responses
  3. Error Handling: Remove credential-related details from user-facing errors
  4. Rate Limiting: Implement request throttling to prevent abuse

Recommendations Summary

Priority Action
🔴 HIGH Implement credential sanitization and validation
🔴 HIGH Add response schema validation (Zod) for all WebSocket responses
🔴 HIGH Sanitize error messages to prevent credential leakage
🟡 MEDIUM Add rate limiting for concurrent requests
🟡 MEDIUM Validate device/group IDs against allowed character set
🟢 LOW Implement structured audit logging
🟢 LOW Add separate connection/request timeout configuration

Appendices

A. Test Coverage Summary

Test Suite Tests Passed Status
timeout.test.ts 4/4 ✅
device-status.test.ts 6/6 ✅
adapter-factory.test.ts 3/3 ✅
contracts.test.ts 5/5 ✅
group-filtering.test.ts 4/4 ✅
mock-data-loader.test.ts 2/2 ✅
integration.test.ts 8/8 ✅
mock-adapter.test.ts 20/20 ✅

B. Dependency Security

Package Version Known Vulnerabilities
ws 8.21.3 None reported (as of 2026-08-25)
zod 3.24.2 None reported (as of 2026-08-25)
axios 1.8.1 None reported (as of 2026-08-25)

C. Protocol References

  • MeshCentral version: 1.2.5 (released 2026-08-12)
  • Control endpoint: /control.ashx (WebSocket)
  • Auth header: x-meshauth (base64 encoded credentials)
  • Actions: serverversion, userinfo, meshes, nodes, events, deviceinfo

Conclusion

The bridge implementation demonstrates a solid understanding of security principles in its design, particularly around TLS validation and allowlist enforcement. However, the current code contains critical gaps in credential handling, input validation, and error sanitization that must be addressed before any live deployment.

Final Verdict: DO NOT ENABLE LIVE TESTING until the three high-severity findings are remediated.


Review completed: 2026-08-25 01:45 EDT
Subagent session: ca174557-cd3e-4d97-90a9-31efee68aebd