feat(security-sidecar): implement a security tooling sidecar with tiered installer and protocol
- Add `zesdex_sec_daemon` module with main entry point for running the security daemon. - Implement `TieredInstaller` for installing security tools from various sources (pip, binaries, gems). - Create a newline-delimited JSON frame protocol for communication between the daemon and tools. - Introduce a `ToolRegistry` for managing and dispatching tool executions. - Add various tools including HTTP, SQLMap, Nuclei, and more with their respective execution logic. - Establish health check and installation commands for tool management. - Include prompts for classifier and quality reviewer to enhance code review and safety checks. - Document the system's tools and guidelines for usage.
This commit is contained in:
@@ -1,15 +1,8 @@
|
||||
use crate::app::state::rest::AppStateRest;
|
||||
use crate::app::state::types::Overlay;
|
||||
|
||||
pub fn handle_bash_submit(state: &mut AppStateRest, command: String) {
|
||||
if !command.is_empty() {
|
||||
let _job = crate::app::bgbash::job::spawn_bash_job(command);
|
||||
let _ = crate::app::bgbash::job::spawn_bash_job(command);
|
||||
state.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn handle_bash_dismiss(state: &mut AppStateRest) {
|
||||
state.misc.overlay = Overlay::None;
|
||||
state.dirty = true;
|
||||
}
|
||||
|
||||
+206
-1
@@ -1,12 +1,217 @@
|
||||
use crate::app::state::rest::AppStateRest;
|
||||
use crate::app::state::types::Overlay;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EditorState {
|
||||
pub path: String,
|
||||
pub content: Vec<String>,
|
||||
pub undo_stack: Vec<Vec<String>>,
|
||||
pub cursor_line: usize,
|
||||
pub cursor_col: usize,
|
||||
pub scroll_offset: usize,
|
||||
pub active: bool,
|
||||
pub mode: EditorMode,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum EditorMode {
|
||||
Normal,
|
||||
Insert,
|
||||
Visual,
|
||||
}
|
||||
|
||||
impl Default for EditorState {
|
||||
fn default() -> Self {
|
||||
EditorState {
|
||||
path: String::new(),
|
||||
content: vec![String::new()],
|
||||
undo_stack: Vec::new(),
|
||||
cursor_line: 0,
|
||||
cursor_col: 0,
|
||||
scroll_offset: 0,
|
||||
active: false,
|
||||
mode: EditorMode::Normal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EditorState {
|
||||
pub fn open(path: String, existing_content: Option<Vec<String>>) -> Self {
|
||||
let content = existing_content.unwrap_or_else(|| vec![String::new()]);
|
||||
EditorState {
|
||||
path,
|
||||
content,
|
||||
active: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change_line(&mut self, text: String) {
|
||||
self.save_undo();
|
||||
if self.cursor_line < self.content.len() {
|
||||
self.content[self.cursor_line] = text;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_line_after(&mut self) {
|
||||
self.save_undo();
|
||||
let pos = (self.cursor_line + 1).min(self.content.len());
|
||||
self.content.insert(pos, String::new());
|
||||
}
|
||||
|
||||
pub fn delete_current_line(&mut self) {
|
||||
if self.content.len() <= 1 {
|
||||
return;
|
||||
}
|
||||
self.save_undo();
|
||||
self.content.remove(self.cursor_line);
|
||||
if self.cursor_line >= self.content.len() {
|
||||
self.cursor_line = self.content.len() - 1;
|
||||
}
|
||||
self.cursor_col = 0;
|
||||
}
|
||||
|
||||
fn save_undo(&mut self) {
|
||||
self.undo_stack.push(self.content.clone());
|
||||
if self.undo_stack.len() > 50 {
|
||||
self.undo_stack.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn undo(&mut self) {
|
||||
if let Some(prev) = self.undo_stack.pop() {
|
||||
self.content = prev;
|
||||
self.cursor_line = self.cursor_line.min(self.content.len().saturating_sub(1));
|
||||
self.cursor_col = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cursor_up(&mut self) {
|
||||
if self.cursor_line > 0 {
|
||||
self.cursor_line -= 1;
|
||||
}
|
||||
self.cursor_col = self.cursor_col.min(
|
||||
self.content.get(self.cursor_line).map(|l| l.len()).unwrap_or(0),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn cursor_down(&mut self) {
|
||||
if self.cursor_line + 1 < self.content.len() {
|
||||
self.cursor_line += 1;
|
||||
}
|
||||
self.cursor_col = self.cursor_col.min(
|
||||
self.content.get(self.cursor_line).map(|l| l.len()).unwrap_or(0),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn cursor_left(&mut self) {
|
||||
if self.cursor_col > 0 {
|
||||
self.cursor_col -= 1;
|
||||
} else if self.cursor_line > 0 {
|
||||
self.cursor_line -= 1;
|
||||
self.cursor_col = self.content.get(self.cursor_line).map(|l| l.len()).unwrap_or(0);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cursor_right(&mut self) {
|
||||
if let Some(line) = self.content.get(self.cursor_line) {
|
||||
if self.cursor_col < line.len() {
|
||||
self.cursor_col += 1;
|
||||
} else if self.cursor_line + 1 < self.content.len() {
|
||||
self.cursor_line += 1;
|
||||
self.cursor_col = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_char(&mut self, c: char) {
|
||||
self.save_undo();
|
||||
if let Some(line) = self.content.get_mut(self.cursor_line) {
|
||||
line.insert(self.cursor_col, c);
|
||||
self.cursor_col += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete_left(&mut self) {
|
||||
self.save_undo();
|
||||
if let Some(line) = self.content.get_mut(self.cursor_line) {
|
||||
if self.cursor_col > 0 {
|
||||
self.cursor_col -= 1;
|
||||
line.remove(self.cursor_col);
|
||||
} else if self.cursor_line > 0 {
|
||||
let prev_len = self.content[self.cursor_line - 1].len();
|
||||
let rest = self.content.remove(self.cursor_line);
|
||||
self.cursor_line -= 1;
|
||||
self.cursor_col = prev_len;
|
||||
self.content[self.cursor_line].push_str(&rest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn join_lines(&mut self) {
|
||||
if self.cursor_line + 1 >= self.content.len() {
|
||||
return;
|
||||
}
|
||||
self.save_undo();
|
||||
let next = self.content.remove(self.cursor_line + 1);
|
||||
self.content[self.cursor_line].push_str(&next);
|
||||
}
|
||||
|
||||
pub fn as_string(&self) -> String {
|
||||
self.content.join("\n")
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
self.active = false;
|
||||
}
|
||||
|
||||
pub fn toggle_mode(&mut self) {
|
||||
self.mode = match self.mode {
|
||||
EditorMode::Normal => EditorMode::Insert,
|
||||
EditorMode::Insert => EditorMode::Normal,
|
||||
EditorMode::Visual => EditorMode::Normal,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppEditorState {
|
||||
pub editor: Option<EditorState>,
|
||||
}
|
||||
|
||||
impl AppEditorState {
|
||||
pub fn new() -> Self {
|
||||
AppEditorState { editor: None }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_editor_input(state: &mut AppStateRest, text: String) {
|
||||
let _ = text;
|
||||
let editor = &mut state.misc.editor;
|
||||
if editor.is_none() {
|
||||
return;
|
||||
}
|
||||
let ed = editor.as_mut().unwrap();
|
||||
for c in text.chars() {
|
||||
match c {
|
||||
'\n' | '\r' => {
|
||||
ed.insert_line_after();
|
||||
ed.cursor_down();
|
||||
ed.cursor_col = 0;
|
||||
}
|
||||
'\t' => {
|
||||
ed.insert_char(' ');
|
||||
ed.insert_char(' ');
|
||||
}
|
||||
_ => {
|
||||
ed.insert_char(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
state.dirty = true;
|
||||
}
|
||||
|
||||
pub fn handle_editor_dismiss(state: &mut AppStateRest) {
|
||||
state.misc.editor = None;
|
||||
state.misc.overlay = Overlay::None;
|
||||
state.dirty = true;
|
||||
}
|
||||
|
||||
+14
-4
@@ -2,13 +2,23 @@ use crate::app::state::rest::AppStateRest;
|
||||
|
||||
pub const EFFORT_LEVELS: &[&str] = &["low", "medium", "high", "xhigh", "max"];
|
||||
|
||||
pub fn current_effort(_state: &AppStateRest) -> usize {
|
||||
1
|
||||
pub fn current_effort(state: &AppStateRest) -> usize {
|
||||
state.misc.effort_level.min(EFFORT_LEVELS.len() - 1)
|
||||
}
|
||||
|
||||
pub fn current_effort_str(state: &AppStateRest) -> &'static str {
|
||||
let idx = current_effort(state);
|
||||
EFFORT_LEVELS[idx]
|
||||
}
|
||||
|
||||
pub fn cycle_effort(state: &mut AppStateRest) {
|
||||
let current = current_effort(state);
|
||||
let next = (current + 1) % EFFORT_LEVELS.len();
|
||||
let _ = next;
|
||||
state.misc.effort_level = (current + 1) % EFFORT_LEVELS.len();
|
||||
state.dirty = true;
|
||||
}
|
||||
|
||||
pub fn set_effort(state: &mut AppStateRest, level: usize) {
|
||||
let clamped = level.min(EFFORT_LEVELS.len() - 1);
|
||||
state.misc.effort_level = clamped;
|
||||
state.dirty = true;
|
||||
}
|
||||
|
||||
@@ -6,14 +6,6 @@ pub fn toggle_security_arm(state: &mut AppStateRest) {
|
||||
state.dirty = true;
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn acknowledge_security(state: &mut AppStateRest) {
|
||||
if !state.misc.security_acknowledged {
|
||||
state.misc.security_acknowledged = true;
|
||||
state.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_security_action(state: &mut AppStateRest, action: &Action) {
|
||||
if let Action::ToggleYoloArm = action {
|
||||
toggle_security_arm(state);
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
use crate::app::runtime::actions::Action;
|
||||
use crate::app::state::rest::AppStateRest;
|
||||
use crate::model::settings::{Settings, InternetMode};
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn apply_settings_action(state: &mut AppStateRest, action: &Action) {
|
||||
if let Action::ToggleYoloArm = action {
|
||||
state.misc.yolo_armed = !state.misc.yolo_armed;
|
||||
state.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cycle_internet_mode(settings: &mut Settings) {
|
||||
settings.internet_mode = match settings.internet_mode {
|
||||
InternetMode::Off => InternetMode::ReadOnly,
|
||||
@@ -17,8 +7,3 @@ pub fn cycle_internet_mode(settings: &mut Settings) {
|
||||
InternetMode::Full => InternetMode::Off,
|
||||
};
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn cycle_review_enabled(settings: &mut Settings) {
|
||||
settings.review_enabled = !settings.review_enabled;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user