Skip to main content

Security Hardening — Phase 9

Phase 9 implements 12 targeted security improvements identified through a gap analysis comparing OpenClaw vulnerabilities with ClawDesk's architecture. All changes compile cleanly (634 tests, 0 failures).

Overview Matrix

IDTitlePriorityCrate(s) Modified
T-01Ed25519 Skill Signature VerificationP0clawdesk-skills
T-02Plugin Sandbox TOCTOU FixP0clawdesk-plugin
T-03Channel Registration AttestationP1clawdesk-channel, clawdesk-gateway
T-04ACL on Message Hot-PathP0clawdesk-gateway
T-05Aho-Corasick Multi-Pattern ScannerP1clawdesk-security
T-06Content-Addressed Skill IdentityP1clawdesk-skills
T-07Typed Channel Config SchemasP2clawdesk-channels
T-08SHA-256 Cryptographic Audit ChainP1clawdesk-security
T-09Hierarchical Rate LimitingP1clawdesk-gateway
T-10Prompt Namespace IsolationP2clawdesk-agents
T-11Manifest Schema VersioningP2clawdesk-skills
T-12Staged Promotion PipelineP2clawdesk-skills

T-01: Ed25519 Skill Signature Verification

File: crates/clawdesk-skills/src/verification.rs (new)

Skills are now cryptographically verified before loading. Each skill manifest can carry a signature and publisher_key field. The verifier checks:

  1. Trust levels: Unsigned < SignedUntrusted < SignedTrusted < Builtin
  2. Builtin skills always get TrustLevel::Builtin (highest trust)
  3. Signed skills with signatures are verified against a trusted publisher key set
  4. Gating: verify_and_gate() rejects skills below a minimum trust level
pub struct SkillVerifier {
trusted_keys: HashSet<String>,
minimum_trust: TrustLevel,
}

// loader.rs integration:
let verification = self.verifier.verify_and_gate(&manifest, &source)?;

Dependencies added: ed25519-dalek = "2", sha2 = "0.10", hex = "0.4"


T-02: Plugin Sandbox TOCTOU Fix

File: crates/clawdesk-plugin/src/sandbox.rs, host.rs

The plugin sandbox resource allocation had a Time-of-Check-Time-of-Use (TOCTOU) race condition. Three allocation methods (try_allocate_memory, try_spawn_task, try_open_fd) used load(Relaxed) + fetch_add(Relaxed) — a non-atomic check-then-act pattern.

Fix: Replaced with compare_exchange_weak CAS loops using Ordering::AcqRel/Acquire:

loop {
let current = self.current_memory_bytes.load(Ordering::Acquire);
let new = current + bytes;
if new > self.config.max_memory_bytes {
return Err(SandboxError::MemoryLimitExceeded { ... });
}
match self.current_memory_bytes.compare_exchange_weak(
current, new, Ordering::AcqRel, Ordering::Acquire,
) {
Ok(_) => break,
Err(_) => continue, // CAS retry
}
}

Host integration: send_to_plugin() now acquires a TaskGuard, checks payload memory budget, and wraps execution in tokio::time::timeout().


T-03: Channel Registration Attestation

File: crates/clawdesk-channel/src/registry.rs (rewritten)

Channel registration now validates and attests:

  • Validation: Display name non-empty, no duplicate channel IDs
  • Capability probing: Auto-detects threading, streaming, reactions, group management from ChannelMeta
  • Provenance tracking: Records capabilities + registration timestamp
  • Return type: register() returns RegistrationResult (Ok/Rejected) instead of ()
pub enum RegistrationResult {
Ok { id: ChannelId, capabilities: ChannelCapabilities },
Rejected { reason: String },
}

T-04: ACL on Message Hot-Path

File: crates/clawdesk-gateway/src/middleware.rs

Three ACL enforcement functions added for the message dispatch hot-path:

  • check_message_acl() — validates user can send messages on a channel
  • check_tool_acl() — validates user can invoke a specific tool
  • check_plugin_acl() — validates user can interact with a plugin

Each maps the request context to (Principal, Resource, Action) and queries the AclManager with O(1) indexed lookup.


T-05: Aho-Corasick Multi-Pattern Scanner

File: crates/clawdesk-security/src/scanner.rs (rewritten)

Replaced the sequential O(p×m) fixed-string scanner with a pre-compiled Aho-Corasick automaton for O(m+z) single-pass scanning:

Tier 0.5 → Aho-Corasick (fixed strings: eval, exec, script, javascript:, etc.)
Tier 1 → Regex patterns (character classes, backreferences)
Tier 2 → Structural checks (oversize content)
Tier 3 → Semantic analysis (placeholder)

Default patterns: eval(, exec(, <script, javascript:, data:text/html, password, -----BEGIN

Dependency added: aho-corasick = "1"


T-06: Content-Addressed Skill Identity

Files: crates/clawdesk-skills/src/definition.rs, loader.rs

Skills are now content-addressed via SHA-256:

  • SkillManifest gains content_hash: Option<String>
  • On load, the SHA-256 of prompt.md is computed and stored
  • Enables deduplication, tamper detection, and cache invalidation

T-07: Typed Channel Config Schemas

File: crates/clawdesk-channels/src/factory.rs

Channel factories now register typed config schemas alongside constructors:

let schema = ConfigSchema::new("telegram")
.required("bot_token", ConfigFieldType::String, "Telegram Bot API token")
.optional("enable_groups", ConfigFieldType::Bool, "Allow group chats");
factory.register_with_schema("telegram", schema, |config| { ... });
  • ConfigSchema::validate() checks required fields and type correctness
  • factory.create() validates config against schema before construction
  • Constructor type changed from Box<dyn Fn> to Arc<dyn Fn> for Clone support
  • All 7 built-in channels have schemas (telegram, discord, slack, whatsapp, matrix, msteams, webchat/internal via register)

T-08: SHA-256 Cryptographic Audit Chain

File: crates/clawdesk-security/src/audit.rs

Replaced the non-cryptographic FNV-1a (64-bit) hash chain with SHA-256 (256-bit):

  • Genesis hash: "0" × 64 (was "0" × 16)
  • Each entry: SHA-256(prev_hash || category || detail || actor || timestamp)
  • epoch_interval config for periodic epoch anchors
  • verify_chain() validates the full chain integrity

T-09: Hierarchical Rate Limiting

File: crates/clawdesk-gateway/src/rate_limiter.rs

Added a 4-tier hierarchical rate limiter that enforces limits at multiple scopes:

TierScopeBurstSustained
0Global1000200/s
1Channel10020/s
2Plugin5010/s
3Skill205/s

check() and check_named() validate requests against all tier limits — the narrowest tier that trips blocks the request.


T-10: Prompt Namespace Isolation

File: crates/clawdesk-agents/src/prompt_isolation.rs (new)

Skill prompt fragments are now wrapped in structured namespace envelopes:

--- BEGIN SKILL [core/web-search] (trust: builtin) ---
Allowed tools: web_search, fetch_url
You can search the web for information.
--- END SKILL [core/web-search] ---

Key types:

  • PromptIsolator — bipartite graph (skill → allowed tools), enforces can_use_tool()
  • IsolatedPrompt — renders namespaced prompt with boundary markers
  • PromptNamespace — skill ID, trust label, fragment, allowed/provided tools

T-11: Manifest Schema Versioning

File: crates/clawdesk-skills/src/definition.rs

SkillManifest now includes schema_version: u32 (default: 1 via #[serde(default)]). This enables:

  • Forward-compatible manifest evolution
  • Migration logic keyed on version number
  • Rejection of unsupported future versions

T-12: Staged Promotion Pipeline

File: crates/clawdesk-skills/src/promotion.rs (new)

Skills progress through a deterministic FSM before activation:

Submitted → Scanned → SandboxTested → Approved → Active
↓ ↓ ↓ ↓
Rejected Rejected Rejected Rejected

Features:

  • PromotionPipeline — tracks entries through stages
  • advance() / reject() — stage transitions with validation
  • RollbackBuffer — circular buffer of N active snapshots for instant rollback
  • at_stage() — query all skills at a given stage

Dependency Changes

Workspace-level (Cargo.toml)

# --- Cryptography ---
ed25519-dalek = { version = "2", features = ["std"] }
sha2 = "0.10"
hex = "0.4"

# --- Text search ---
aho-corasick = "1"

Crate-level additions

CrateNew Dependencies
clawdesk-skillsed25519-dalek, sha2, hex
clawdesk-securitysha2, hex, aho-corasick