mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-31 08:39:24 +00:00
* test: add failing tests for Discord signature validation and capabilities alias (Red phase) TDD Red phase for #148. Adds 19 tests across 4 categories: - Category 1: CredentialLocationSchema header_name alias (2 failing) - Category 2: Ed25519 signature verification (3 failing) - Category 3: Router signature key management (2 failing) - Category 5: Discord capabilities public_key setup (1 failing) All 8 failures are expected — stubs return false/None by design. Implementation will follow in Green phase. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: add Discord Ed25519 signature verification and capabilities alias (#148) Implement the Green phase for Discord channel security fixes: - Add real Ed25519 signature verification in signature.rs using ed25519-dalek - Add #[serde(alias = "header_name")] to CredentialLocationSchema::Header for backward compatibility with external JSON files - Add signature_keys storage to WasmChannelRouter (register/get/unregister) - Add discord_public_key to discord.capabilities.json setup.required_secrets - Add nested capabilities resolution to CapabilitiesFile for channel-level JSON compatibility Co-Authored-By: Claude Opus 4.6 <[email protected]> * style: address PR #372 review comments - Fix invalid hex character in test fake_pub_key (router.rs) - Simplify signature parsing with from_slice/try_from (signature.rs) - Use idiomatic Option::or for nested capability merging (capabilities_schema.rs) Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: enforce signature verification, staleness check, key validation, recursive resolve Address PR #372 review feedback: - Wire verify_discord_signature() into webhook_handler with Ed25519 signature + timestamp staleness check (5s window via now_secs param) - Validate Ed25519 keys in register_signature_key() (hex decode + VerifyingKey::try_from) before storing, return Result<(), String> - Recursively resolve nested capabilities in resolve_nested() - Add 25 new tests: 8 staleness, 6 key validation, 7 webhook integration (tower::oneshot), 4 resolve_nested edge cases - Fix pre-existing clippy warning in signal.rs Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: wire register_signature_key() into all channel loading paths The Ed25519 signature key registration was implemented and tested but never called from production code. All three channel loading paths (setup_wasm_channels, activate_wasm_channel, refresh_active_channel) now read the public key from the secrets store and register it with the webhook router, enabling Discord signature verification. Adds `signature_key_secret_name` field to WebhookSchema so channels can declare which secret contains their Ed25519 public key. Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
a24fd3e8a3
commit
a7c0be7f1b
@@ -1796,6 +1796,7 @@ impl ExtensionManager {
|
||||
let channel_name = loaded.name().to_string();
|
||||
let webhook_secret_name = loaded.webhook_secret_name();
|
||||
let secret_header = loaded.webhook_secret_header().map(|s| s.to_string());
|
||||
let sig_key_secret_name = loaded.signature_key_secret_name();
|
||||
|
||||
// Get webhook secret from secrets store
|
||||
let webhook_secret = self
|
||||
@@ -1861,6 +1862,26 @@ impl ExtensionManager {
|
||||
)
|
||||
.await;
|
||||
tracing::info!(channel = %channel_name, "Registered hot-activated channel with webhook router");
|
||||
|
||||
// Register Ed25519 signature key if declared in capabilities
|
||||
if let Some(ref sig_key_name) = sig_key_secret_name
|
||||
&& let Ok(key_secret) = self
|
||||
.secrets
|
||||
.get_decrypted(&self.user_id, sig_key_name)
|
||||
.await
|
||||
{
|
||||
match wasm_channel_router
|
||||
.register_signature_key(&channel_name, key_secret.expose())
|
||||
.await
|
||||
{
|
||||
Ok(()) => {
|
||||
tracing::info!(channel = %channel_name, "Registered signature key for hot-activated channel")
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!(channel = %channel_name, error = %e, "Failed to register signature key")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Inject credentials
|
||||
@@ -1996,6 +2017,37 @@ impl ExtensionManager {
|
||||
existing_channel.update_config(config_updates).await;
|
||||
}
|
||||
|
||||
// Also refresh signature key in the router
|
||||
let sig_key_secret_name = {
|
||||
let cap_path = self
|
||||
.wasm_channels_dir
|
||||
.join(format!("{}.capabilities.json", name));
|
||||
match tokio::fs::read(&cap_path).await {
|
||||
Ok(bytes) => crate::channels::wasm::ChannelCapabilitiesFile::from_bytes(&bytes)
|
||||
.ok()
|
||||
.and_then(|f| f.signature_key_secret_name().map(|s| s.to_string())),
|
||||
Err(_) => None,
|
||||
}
|
||||
};
|
||||
if let Some(ref sig_key_name) = sig_key_secret_name
|
||||
&& let Ok(key_secret) = self
|
||||
.secrets
|
||||
.get_decrypted(&self.user_id, sig_key_name)
|
||||
.await
|
||||
{
|
||||
match router
|
||||
.register_signature_key(name, key_secret.expose())
|
||||
.await
|
||||
{
|
||||
Ok(()) => {
|
||||
tracing::info!(channel = %name, "Refreshed signature verification key")
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!(channel = %name, error = %e, "Failed to refresh signature key")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh tunnel_url in case it wasn't set at startup
|
||||
if let Some(ref tunnel_url) = self.tunnel_url {
|
||||
let mut config_updates = std::collections::HashMap::new();
|
||||
|
||||
Reference in New Issue
Block a user