Skip to content

Commit fcfd4d2

Browse files
authored
Merge pull request #52 from 100monkeys-ai/copilot/fix-tool-routing-errors
Fix semantic misuse of `SignatureVerificationFailed`, add `JudgeTimeout` variant, harden path traversal prevention
2 parents 5753cf0 + a6484e5 commit fcfd4d2

File tree

2 files changed

+20
-7
lines changed

2 files changed

+20
-7
lines changed

orchestrator/core/src/application/tool_invocation_service.rs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -144,13 +144,11 @@ impl ToolInvocationService {
144144
.route_tool(session.execution_id, &tool_name)
145145
.await
146146
.map_err(|e| {
147-
SmcpSessionError::SignatureVerificationFailed(format!("Routing error: {}", e))
147+
SmcpSessionError::MalformedPayload(format!("Routing error: {}", e))
148148
})?;
149149

150150
let server = self.tool_router.get_server(server_id).await.ok_or(
151-
SmcpSessionError::SignatureVerificationFailed(
152-
"Server vanished after routing".to_string(),
153-
),
151+
SmcpSessionError::MalformedPayload("Server vanished after routing".to_string()),
154152
)?;
155153

156154
// 4. Execute based on ExecutionMode (Gateway Retrofit)
@@ -337,7 +335,7 @@ impl ToolInvocationService {
337335

338336
loop {
339337
if attempts >= max_attempts {
340-
return Err(SmcpSessionError::SignatureVerificationFailed(
338+
return Err(SmcpSessionError::JudgeTimeout(
341339
format!(
342340
"Inner-loop semantic judge '{}' timed out after {} seconds.",
343341
judge_agent,
@@ -993,7 +991,7 @@ fn sanitize_segment(input: &str) -> String {
993991
return "unversioned".to_string();
994992
}
995993

996-
trimmed
994+
let sanitized: String = trimmed
997995
.chars()
998996
.map(|c| {
999997
if c.is_ascii_alphanumeric() || c == '.' || c == '-' || c == '_' {
@@ -1002,7 +1000,19 @@ fn sanitize_segment(input: &str) -> String {
10021000
'_'
10031001
}
10041002
})
1005-
.collect()
1003+
.collect();
1004+
1005+
// Prevent path traversal patterns after character substitution.
1006+
// Treat empty or traversal-like segments as a safe default.
1007+
if sanitized.is_empty()
1008+
|| sanitized == "."
1009+
|| sanitized == ".."
1010+
|| sanitized.contains("..")
1011+
{
1012+
"unversioned".to_string()
1013+
} else {
1014+
sanitized
1015+
}
10061016
}
10071017

10081018
fn path_to_string(path: &Path) -> String {

orchestrator/core/src/domain/smcp_session.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ pub enum SmcpSessionError {
8888
MalformedPayload(String),
8989
/// The Ed25519 signature on the envelope did not verify against the session's stored public key.
9090
SignatureVerificationFailed(String),
91+
/// A semantic judge agent did not respond within the allotted timeout window.
92+
JudgeTimeout(String),
9193
}
9294

9395
impl std::fmt::Display for SmcpSessionError {
@@ -100,6 +102,7 @@ impl std::fmt::Display for SmcpSessionError {
100102
Self::SignatureVerificationFailed(e) => {
101103
write!(f, "Signature verification failed: {}", e)
102104
}
105+
Self::JudgeTimeout(msg) => write!(f, "Judge timed out: {}", msg),
103106
}
104107
}
105108
}

0 commit comments

Comments
 (0)