ghidra_worker_ctl/
token.rs1use std::io;
8use std::path::Path;
9
10#[derive(Debug, Clone)]
12pub struct ConnectionToken(String);
13
14impl ConnectionToken {
15 pub fn generate() -> Self {
18 let bytes = uuid::Uuid::new_v4().into_bytes();
19 let mut s = String::with_capacity(32);
20 for b in bytes {
21 s.push_str(&format!("{b:02x}"));
22 }
23 ConnectionToken(s)
24 }
25
26 pub fn as_str(&self) -> &str {
27 &self.0
28 }
29
30 pub fn verify(&self, candidate: &str) -> bool {
34 let a = self.0.as_bytes();
35 let b = candidate.as_bytes();
36 if a.len() != b.len() {
37 return false;
38 }
39 let mut diff: u8 = 0;
40 for (x, y) in a.iter().zip(b.iter()) {
41 diff |= x ^ y;
42 }
43 diff == 0
44 }
45
46 pub fn write_to_file(&self, path: &Path) -> io::Result<()> {
52 std::fs::write(path, self.0.as_bytes())?;
53 #[cfg(unix)]
54 {
55 use std::os::unix::fs::PermissionsExt;
56 std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o600))?;
57 }
58 Ok(())
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn generated_token_is_128_bit_hex() {
68 let t = ConnectionToken::generate();
69 assert_eq!(t.as_str().len(), 32); assert!(t.as_str().chars().all(|c| c.is_ascii_hexdigit()));
71 }
72
73 #[test]
74 fn two_tokens_differ() {
75 assert_ne!(
76 ConnectionToken::generate().as_str(),
77 ConnectionToken::generate().as_str()
78 );
79 }
80
81 #[test]
82 fn verify_accepts_own_value_rejects_others_and_length_mismatch() {
83 let t = ConnectionToken::generate();
84 assert!(t.verify(t.as_str()));
85 assert!(!t.verify("deadbeef"));
86 assert!(!t.verify(&(t.as_str().to_string() + "x")));
87 assert!(!t.verify(""));
88 }
89
90 #[test]
91 fn write_to_file_roundtrips_the_exact_value() {
92 let dir = std::env::temp_dir().join(format!("ghidra-mcp-tok-{}", uuid::Uuid::new_v4()));
93 std::fs::create_dir_all(&dir).unwrap();
94 let path = dir.join("t.token");
95 let t = ConnectionToken::generate();
96 t.write_to_file(&path).unwrap();
97 let read = std::fs::read_to_string(&path).unwrap();
98 assert_eq!(read, t.as_str());
99 std::fs::remove_dir_all(&dir).ok();
100 }
101}