Skip to content

Commit fa6f83d

Browse files
doublegateclaude
andcommitted
fix(deps): resolve Dependabot security alerts and update WRAITH-RedOps dependencies
Security Fixes: - Update sqlx from 0.7 to 0.8 (CVE: Binary Protocol Misinterpretation, medium severity) - Update vite from 4.4.0 to 7.3.1 (CVE: server.fs bypass, low severity x2) WRAITH-RedOps Updates: - Migrate operator-client from Tauri 1.5 to Tauri 2.2 - Migrate operator-client frontend to React 19 + TypeScript 5.9 + Vite 7.3 - Update team-server to Rust 2024 edition with modern dependencies - Update gRPC stack (tonic 0.12, prost 0.13) - Update web framework stack (axum 0.8, tower 0.5, tower-http 0.6) - Apply project-wide formatting standards via cargo fmt - Fix deprecated tonic_build::compile -> compile_protos Resolves: Dependabot alerts #1, #2, #3 Related: PR #48 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent bc80136 commit fa6f83d

File tree

15 files changed

+684
-435
lines changed

15 files changed

+684
-435
lines changed
Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,47 @@
11
{
22
"name": "wraith-redops-client",
33
"private": true,
4-
"version": "0.0.0",
4+
"version": "2.2.5",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",
8-
"build": "tsc && vite build",
8+
"build": "tsc -b && vite build",
99
"preview": "vite preview",
10-
"tauri": "tauri"
10+
"tauri": "tauri",
11+
"lint": "eslint ."
1112
},
1213
"dependencies": {
13-
"@tauri-apps/api": "^1.5.0",
14-
"react": "^18.2.0",
15-
"react-dom": "^18.2.0",
14+
"@tailwindcss/vite": "^4.1.17",
15+
"@tauri-apps/api": "^2.9.1",
16+
"@tauri-apps/plugin-dialog": "^2.4.2",
17+
"@tauri-apps/plugin-fs": "^2.4.4",
18+
"@tauri-apps/plugin-shell": "^2.4.2",
19+
"react": "^19.2.0",
20+
"react-dom": "^19.2.0",
1621
"xterm": "^5.3.0",
1722
"xterm-addon-fit": "^0.8.0",
18-
"zustand": "^4.4.0",
23+
"zustand": "^5.0.9",
1924
"@tanstack/react-query": "^5.0.0",
2025
"lucide-react": "^0.290.0",
2126
"clsx": "^2.0.0",
2227
"tailwind-merge": "^2.0.0"
2328
},
2429
"devDependencies": {
25-
"@tauri-apps/cli": "^1.5.0",
26-
"@types/react": "^18.2.0",
27-
"@types/react-dom": "^18.2.0",
28-
"@vitejs/plugin-react": "^4.0.0",
29-
"autoprefixer": "^10.4.16",
30-
"postcss": "^8.4.31",
31-
"tailwindcss": "^3.3.0",
32-
"typescript": "^5.0.2",
33-
"vite": "^4.4.0"
30+
"@eslint/js": "^9.39.1",
31+
"@tauri-apps/cli": "^2.0.0",
32+
"@types/node": "^24.10.9",
33+
"@types/react": "^19.2.5",
34+
"@types/react-dom": "^19.2.3",
35+
"@vitejs/plugin-react": "^5.1.1",
36+
"autoprefixer": "^10.4.22",
37+
"eslint": "^9.39.1",
38+
"eslint-plugin-react-hooks": "^7.0.1",
39+
"eslint-plugin-react-refresh": "^0.4.24",
40+
"globals": "^16.5.0",
41+
"postcss": "^8.5.6",
42+
"tailwindcss": "^4.1.17",
43+
"typescript": "~5.9.3",
44+
"typescript-eslint": "^8.46.4",
45+
"vite": "^7.3.1"
3446
}
3547
}
Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,52 @@
11
[package]
22
name = "wraith-redops-client"
3-
version = "0.0.0"
3+
version = "2.2.5"
44
description = "WRAITH RedOps Operator Console"
5-
authors = ["you"]
6-
edition = "2021"
5+
authors = ["WRAITH Protocol Contributors"]
6+
license = "MIT OR Apache-2.0"
7+
repository = "https://github.com/doublegate/WRAITH-Protocol"
8+
edition = "2024"
9+
rust-version = "1.88"
10+
11+
[lib]
12+
name = "wraith_redops_client_lib"
13+
crate-type = ["staticlib", "cdylib", "rlib"]
14+
15+
[[bin]]
16+
name = "wraith-redops-client"
17+
path = "src/main.rs"
718

819
[build-dependencies]
9-
tauri-build = { version = "1.5", features = [] }
10-
tonic-build = "0.10"
20+
tauri-build = { version = "2.2", features = [] }
21+
tonic-build = "0.12"
1122

1223
[dependencies]
13-
tauri = { version = "1.5", features = ["shell-open"] }
24+
# Tauri 2.x
25+
tauri = { version = "2.2", features = [] }
26+
tauri-plugin-dialog = "2.2"
27+
tauri-plugin-fs = "2.2"
28+
tauri-plugin-shell = "2.2"
29+
30+
# Serialization
1431
serde = { version = "1.0", features = ["derive"] }
1532
serde_json = "1.0"
16-
tonic = "0.10"
17-
prost = "0.12"
18-
prost-types = "0.12"
33+
34+
# gRPC
35+
tonic = "0.12"
36+
prost = "0.13"
37+
prost-types = "0.13"
38+
39+
# Async runtime
1940
tokio = { version = "1", features = ["full"] }
2041

42+
# Logging
43+
tracing = "0.1"
44+
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
45+
46+
# WRAITH Protocol
47+
wraith-core = { path = "../../../../crates/wraith-core" }
48+
wraith-crypto = { path = "../../../../crates/wraith-crypto" }
49+
2150
[features]
51+
default = ["custom-protocol"]
2252
custom-protocol = ["tauri/custom-protocol"]

clients/wraith-redops/operator-client/src-tauri/build.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
22
tonic_build::configure()
33
.build_server(false)
44
.build_client(true) // This is the client
5-
.compile(
6-
&["../../proto/redops.proto"],
7-
&["../../proto"],
8-
)?;
5+
.compile_protos(&["../../proto/redops.proto"], &["../../proto"])?;
96
tauri_build::build();
107
Ok(())
118
}
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
//! WRAITH RedOps Operator Console
2+
//!
3+
//! A Tauri desktop application for red team operations.
4+
//! Provides a GUI interface for managing campaigns, implants, and commands.
5+
//!
6+
//! ## Features
7+
//! - Campaign management
8+
//! - Implant monitoring
9+
//! - Command execution
10+
//! - Real-time status updates
11+
//!
12+
//! ## Architecture
13+
//! - Frontend: React 19 + TypeScript + Tailwind CSS v4
14+
//! - Backend: Tauri 2.x + Rust
15+
//! - Communication: gRPC to team server
16+
17+
use serde::{Deserialize, Serialize};
18+
use tauri::State;
19+
use tokio::sync::Mutex;
20+
use tonic::transport::Channel;
21+
22+
// Import generated protos
23+
pub mod wraith {
24+
pub mod redops {
25+
tonic::include_proto!("wraith.redops");
26+
}
27+
}
28+
use wraith::redops::operator_service_client::OperatorServiceClient;
29+
use wraith::redops::*;
30+
31+
// JSON-friendly wrapper types for frontend communication
32+
#[derive(Debug, Clone, Serialize, Deserialize)]
33+
pub struct CampaignJson {
34+
pub id: String,
35+
pub name: String,
36+
pub description: String,
37+
pub status: String,
38+
pub implant_count: i32,
39+
pub active_implant_count: i32,
40+
}
41+
42+
impl From<Campaign> for CampaignJson {
43+
fn from(c: Campaign) -> Self {
44+
Self {
45+
id: c.id,
46+
name: c.name,
47+
description: c.description,
48+
status: c.status,
49+
implant_count: c.implant_count,
50+
active_implant_count: c.active_implant_count,
51+
}
52+
}
53+
}
54+
55+
#[derive(Debug, Clone, Serialize, Deserialize)]
56+
pub struct ImplantJson {
57+
pub id: String,
58+
pub campaign_id: String,
59+
pub hostname: String,
60+
pub internal_ip: String,
61+
pub external_ip: String,
62+
pub os_type: String,
63+
pub os_version: String,
64+
pub architecture: String,
65+
pub username: String,
66+
pub domain: String,
67+
pub privileges: String,
68+
pub implant_version: String,
69+
pub checkin_interval: i32,
70+
pub jitter_percent: i32,
71+
pub status: String,
72+
}
73+
74+
impl From<Implant> for ImplantJson {
75+
fn from(i: Implant) -> Self {
76+
Self {
77+
id: i.id,
78+
campaign_id: i.campaign_id,
79+
hostname: i.hostname,
80+
internal_ip: i.internal_ip,
81+
external_ip: i.external_ip,
82+
os_type: i.os_type,
83+
os_version: i.os_version,
84+
architecture: i.architecture,
85+
username: i.username,
86+
domain: i.domain,
87+
privileges: i.privileges,
88+
implant_version: i.implant_version,
89+
checkin_interval: i.checkin_interval,
90+
jitter_percent: i.jitter_percent,
91+
status: i.status,
92+
}
93+
}
94+
}
95+
96+
struct ClientState {
97+
client: Mutex<Option<OperatorServiceClient<Channel>>>,
98+
}
99+
100+
#[tauri::command]
101+
async fn connect_to_server(
102+
address: String,
103+
state: State<'_, ClientState>,
104+
) -> Result<String, String> {
105+
let endpoint = if address.starts_with("http") {
106+
address
107+
} else {
108+
format!("http://{}", address)
109+
};
110+
111+
let client = OperatorServiceClient::connect(endpoint)
112+
.await
113+
.map_err(|e| format!("Failed to connect: {}", e))?;
114+
115+
let mut lock = state.client.lock().await;
116+
*lock = Some(client);
117+
118+
Ok("Connected successfully".to_string())
119+
}
120+
121+
#[tauri::command]
122+
async fn create_campaign(
123+
name: String,
124+
description: String,
125+
state: State<'_, ClientState>,
126+
) -> Result<String, String> {
127+
let mut lock = state.client.lock().await;
128+
let client = lock.as_mut().ok_or("Not connected")?;
129+
130+
let request = tonic::Request::new(CreateCampaignRequest {
131+
name,
132+
description,
133+
roe_document: vec![],
134+
roe_signature: vec![],
135+
});
136+
137+
let response = client
138+
.create_campaign(request)
139+
.await
140+
.map_err(|e| format!("gRPC error: {}", e))?;
141+
142+
let campaign_json: CampaignJson = response.into_inner().into();
143+
serde_json::to_string(&campaign_json).map_err(|e| e.to_string())
144+
}
145+
146+
#[tauri::command]
147+
async fn list_implants(state: State<'_, ClientState>) -> Result<String, String> {
148+
let mut lock = state.client.lock().await;
149+
let client = lock.as_mut().ok_or("Not connected")?;
150+
151+
let request = tonic::Request::new(ListImplantsRequest {
152+
campaign_id: String::new(), // List all for now
153+
status_filter: String::new(),
154+
page_size: 100,
155+
page_token: String::new(),
156+
});
157+
158+
let response = client
159+
.list_implants(request)
160+
.await
161+
.map_err(|e| format!("gRPC error: {}", e))?;
162+
163+
let implants: Vec<ImplantJson> = response
164+
.into_inner()
165+
.implants
166+
.into_iter()
167+
.map(|i| i.into())
168+
.collect();
169+
170+
serde_json::to_string(&implants).map_err(|e| e.to_string())
171+
}
172+
173+
#[tauri::command]
174+
async fn send_command(
175+
implant_id: String,
176+
command_type: String,
177+
payload: String,
178+
state: State<'_, ClientState>,
179+
) -> Result<String, String> {
180+
let mut lock = state.client.lock().await;
181+
let client = lock.as_mut().ok_or("Not connected")?;
182+
183+
let request = tonic::Request::new(SendCommandRequest {
184+
implant_id,
185+
command_type,
186+
payload: payload.into_bytes(),
187+
priority: 1,
188+
timeout_seconds: 30,
189+
});
190+
191+
let response = client
192+
.send_command(request)
193+
.await
194+
.map_err(|e| format!("gRPC error: {}", e))?;
195+
196+
Ok(response.into_inner().id)
197+
}
198+
199+
/// Initialize and run the Tauri application
200+
#[cfg_attr(mobile, tauri::mobile_entry_point)]
201+
pub fn run() {
202+
// Initialize logging
203+
if std::env::var("RUST_LOG").is_err() {
204+
// SAFETY: This is called at the start of main before any threads are spawned
205+
unsafe { std::env::set_var("RUST_LOG", "info") };
206+
}
207+
tracing_subscriber::fmt::init();
208+
209+
tracing::info!("Starting WRAITH RedOps Operator Console");
210+
tracing::warn!("IMPORTANT: This tool is for AUTHORIZED red team operations ONLY");
211+
212+
tauri::Builder::default()
213+
.plugin(tauri_plugin_dialog::init())
214+
.plugin(tauri_plugin_fs::init())
215+
.plugin(tauri_plugin_shell::init())
216+
.manage(ClientState {
217+
client: Mutex::new(None),
218+
})
219+
.invoke_handler(tauri::generate_handler![
220+
connect_to_server,
221+
create_campaign,
222+
list_implants,
223+
send_command
224+
])
225+
.run(tauri::generate_context!())
226+
.expect("error while running tauri application");
227+
}
228+
229+
#[cfg(test)]
230+
mod tests {
231+
#[test]
232+
fn test_module_compiles() {
233+
// This test verifies that the module compiles correctly
234+
assert!(true);
235+
}
236+
}

0 commit comments

Comments
 (0)