#![allow(clippy::await_holding_lock)]
use super::verdicts::normalize_file_verdict;
use super::*;
use crate::config::{LLMConfig, ResolvedConfig, ThresholdsConfig};
use crate::types::FindingTier;
use std::env;
use std::io::{Read, Write};
use std::net::{Shutdown, TcpListener};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc;
use std::sync::{Arc, Mutex, OnceLock};
use std::thread;
use std::time::Duration;
static ENV_LOCK: OnceLock<Mutex<()>> = OnceLock::new();
fn cfg(endpoint: &str) -> ResolvedConfig {
ResolvedConfig {
thresholds: ThresholdsConfig::default(),
ignore: vec![],
generic_names: vec![],
generic_file_names: vec![],
model: "test-model".to_string(),
llm: LLMConfig {
system_context: String::new(),
endpoint: endpoint.to_string(),
},
}
}
fn spawn_openai_style_server(body: &'static str) -> (String, Arc<AtomicUsize>) {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
let hits = Arc::new(AtomicUsize::new(0));
let hits_clone = Arc::clone(&hits);
let (ready_tx, ready_rx) = mpsc::channel();
thread::spawn(move || {
let _ = ready_tx.send(());
loop {
let Ok((mut stream, _)) = listener.accept() else {
return;
};
let mut buf = [0u8; 16384];
let Ok(n) = stream.read(&mut buf) else {
continue;
};
if n == 0 {
continue;
}
hits_clone.fetch_add(1, Ordering::SeqCst);
let response = format!(
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
body.len(),
body
);
let _ = stream.write_all(response.as_bytes());
let _ = stream.flush();
let _ = stream.shutdown(Shutdown::Both);
}
});
let _ = ready_rx.recv();
thread::sleep(Duration::from_millis(50));
(format!("http://{}", addr), hits)
}
fn spawn_openai_style_server_sequence(bodies: Vec<&'static str>) -> (String, Arc<AtomicUsize>) {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
let hits = Arc::new(AtomicUsize::new(0));
let hits_clone = Arc::clone(&hits);
let (ready_tx, ready_rx) = mpsc::channel();
thread::spawn(move || {
let _ = ready_tx.send(());
let mut idx = 0usize;
let mut last_body = bodies.last().copied().unwrap_or("");
loop {
let body = if idx < bodies.len() {
let body = bodies[idx];
idx += 1;
last_body = body;
body
} else {
last_body
};
loop {
let Ok((mut stream, _)) = listener.accept() else {
return;
};
let mut buf = [0u8; 16384];
let Ok(n) = stream.read(&mut buf) else {
continue;
};
if n == 0 {
continue;
}
hits_clone.fetch_add(1, Ordering::SeqCst);
let response = format!(
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
body.len(),
body
);
let _ = stream.write_all(response.as_bytes());
let _ = stream.flush();
let _ = stream.shutdown(Shutdown::Both);
break;
}
}
});
let _ = ready_rx.recv();
(format!("http://{}", addr), hits)
}
fn spawn_openai_style_server_with_capture(
body: &'static str,
) -> (String, Arc<AtomicUsize>, Arc<Mutex<String>>) {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
let hits = Arc::new(AtomicUsize::new(0));
let hits_clone = Arc::clone(&hits);
let captured = Arc::new(Mutex::new(String::new()));
let captured_clone = Arc::clone(&captured);
let (ready_tx, ready_rx) = mpsc::channel();
thread::spawn(move || {
let _ = ready_tx.send(());
loop {
let Ok((mut stream, _)) = listener.accept() else {
return;
};
let mut buf = [0u8; 16384];
let Ok(n) = stream.read(&mut buf) else {
continue;
};
if n == 0 {
continue;
}
hits_clone.fetch_add(1, Ordering::SeqCst);
if let Ok(mut slot) = captured_clone.lock() {
*slot = String::from_utf8_lossy(&buf[..n]).to_string();
}
let response = format!(
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
body.len(),
body
);
let _ = stream.write_all(response.as_bytes());
let _ = stream.flush();
let _ = stream.shutdown(Shutdown::Both);
}
});
let _ = ready_rx.recv();
(format!("http://{}", addr), hits, captured)
}
fn spawn_http_status_server(status: u16, body: &'static str) -> (String, Arc<AtomicUsize>) {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
let hits = Arc::new(AtomicUsize::new(0));
let hits_clone = Arc::clone(&hits);
let (ready_tx, ready_rx) = mpsc::channel();
thread::spawn(move || {
let _ = ready_tx.send(());
loop {
let Ok((mut stream, _)) = listener.accept() else {
return;
};
let mut buf = [0u8; 16384];
let Ok(n) = stream.read(&mut buf) else {
continue;
};
if n == 0 {
continue;
}
hits_clone.fetch_add(1, Ordering::SeqCst);
let response = format!(
"HTTP/1.1 {} ERROR\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
status,
body.len(),
body
);
let _ = stream.write_all(response.as_bytes());
let _ = stream.flush();
let _ = stream.shutdown(Shutdown::Both);
}
});
let _ = ready_rx.recv();
(format!("http://{}", addr), hits)
}
fn spawn_http_status_sequence_server(
statuses: Vec<u16>,
body: &'static str,
) -> (String, Arc<AtomicUsize>) {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
let hits = Arc::new(AtomicUsize::new(0));
let hits_clone = Arc::clone(&hits);
let (ready_tx, ready_rx) = mpsc::channel();
thread::spawn(move || {
let _ = ready_tx.send(());
let mut idx = 0usize;
let mut last_status = statuses.last().copied().unwrap_or(500);
loop {
let status = if idx < statuses.len() {
let status = statuses[idx];
idx += 1;
last_status = status;
status
} else {
last_status
};
let Ok((mut stream, _)) = listener.accept() else {
return;
};
let mut buf = [0u8; 16384];
let Ok(n) = stream.read(&mut buf) else {
continue;
};
if n == 0 {
continue;
}
hits_clone.fetch_add(1, Ordering::SeqCst);
let response = format!(
"HTTP/1.1 {} ERROR\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
status,
body.len(),
body
);
let _ = stream.write_all(response.as_bytes());
let _ = stream.flush();
let _ = stream.shutdown(Shutdown::Both);
}
});
let _ = ready_rx.recv();
(format!("http://{}", addr), hits)
}
#[path = "analyzer/evidence.rs"]
mod evidence;
#[path = "analyzer/normalization.rs"]
mod normalization;
#[path = "analyzer/repository.rs"]
mod repository;
#[path = "analyzer/support.rs"]
mod support;
#[path = "analyzer/surfaces.rs"]
mod surfaces;
#[path = "analyzer/transport.rs"]
mod transport;