From 6d92032d066e363c2e49def6ca39c717824954d4 Mon Sep 17 00:00:00 2001 From: Pyro57000 Date: Mon, 2 Jun 2025 12:18:59 -0500 Subject: [PATCH] added some functions to switch between managing personal projects and managing work projects as well as the ability to separate your current project list into work and personal. --- pentest_tool/src/cli.rs | 47 ++++++- pentest_tool/src/install.rs | 2 +- pentest_tool/src/main.rs | 7 +- pentest_tool/src/project_controls.rs | 187 +++++++++++++++++++++++---- 4 files changed, 209 insertions(+), 34 deletions(-) diff --git a/pentest_tool/src/cli.rs b/pentest_tool/src/cli.rs index 68d06f0..c8bc88d 100644 --- a/pentest_tool/src/cli.rs +++ b/pentest_tool/src/cli.rs @@ -1,11 +1,16 @@ +use std::fs::read_to_string; +use std::fs::OpenOptions; use std::path::PathBuf; use std::process::exit; +use std::io::Write; use std::thread::JoinHandle; use chrono::Datelike; use clearscreen::clear; use clearscreen; use chrono::Local; use colored::Colorize; +use crate::print_error; +use crate::print_informational; use crate::Project; use crate::project_controls; use crate::box_controls; @@ -64,6 +69,9 @@ fn help(command: Option){ "brute force subdomains"| "bsd" | "gobuster dns" | "gd" => lines.push("brute force subdomains||bsd,gobuster dns, gd||this command will run gobuster in the project's distrobox and save the results to your notes.".to_owned()), "dns enumeration" | "de" | "all dns stuff" | "ads" | "dns stuff" | "ds" => lines.push("dns enumeration||de, all dns stuff, ads, dns stuff, de||This command will perform both dns record enumeration with dnsrecon, and subdomain enumeration using gobster inside of your distrobox and save the output to your notes.".to_owned()), "modify tool config" | "mtc" => lines.push("modify tool config|| mtc||This command lets you modify the tool's configuration.".to_owned()), + "separate work and personal projects" | "swpp" | "separate projects" | "seppro" => lines.push("separate work and personal||swpp, separate projects, seppro||This command lets you separate work and personal projects into separate config files. This allows you to load personal and work projects separately!".to_owned()), + "switch to personal projects" | "switch personal" => lines.push("switch to personal projects||switch personal||This command lets you switch which config file is loaded to the personal config file.".to_owned()), + "switch to work projects" | "switch work" => lines.push("switch to work projects||switch work||This command lets you switch to load the work projects.".to_owned()), _ => () } tableize(lines); @@ -108,6 +116,9 @@ fn help(command: Option){ "brute force subdomain||bsd, gobuster dns, gd".to_owned(), "dns enumeration||de, all dns stuff, ads, dns stuff, ds".to_owned(), "modify tool config||mtc".to_owned(), + "separate work and personal projects||swpp, separate projects, seppro".to_owned(), + "switch to personal projects||switch personal".to_owned(), + "switch to work projects||switch work".to_owned(), "help||?, -h".to_owned()]; println!("available commands:"); tableize(lines); @@ -243,6 +254,9 @@ pub fn run_command(cmd: String, "dns squatting scan" | "dnstwist" | "dss" => {let twist_handle = enumeration::dns_squatting(&active_project, None, true); return twist_handle}, "print report information" | "pri" => {info_controls::print_report_information(&active_project); return None;}, "modify tool config" | "mtc" => {configuration::generate_tool_config(&config_path); return None;}, + "separate work and personal projects" | "swpp" | "separate projects" | "seppro" => {project_controls::separate_personal_work_projects(&config_path); return None;} + "switch to personal projects" | "switch personal" => {let mut project_load_res = project_controls::swith_to_personal(&config_path); if project_load_res.is_some(){projects.clear();for project in project_load_res.unwrap(){projects.push(project);}}; return None;}, + "switch to work projects" | "switch work" => {let mut project_load_res = project_controls::swith_to_work(&config_path); if project_load_res.is_some(){projects.clear();for project in project_load_res.unwrap(){projects.push(project);}}; return None;} _ => {help(None); println!("\n\n unknown command."); return None;} } } @@ -266,7 +280,7 @@ pub fn cli(interactive: bool, upcoming_notes: &PathBuf, password_spray_file: &PathBuf, fingerprint: bool, - vault_name: String) { + vault_name: String,) { let mut threads = Vec::new(); if interactive{ let mut loopize = true; @@ -339,6 +353,7 @@ for help enter help or ?. for information about a specific command enter help (c ", active_project.customer.green(), active_project.project_name.green(), active_project.stage.green(), active_project.files_folder.display().to_string().green(), active_project.notes_folder.display().to_string().green(), active_project.boxname.green(), "coming soon".red()); + println!("{}", config_path.display()); let prompt = format!("\n{}:{}\nCommand?", active_project.customer.custom_color((255,165,0)), active_project.project_name.custom_color((255,165,0))); let command = get_user_input(&prompt); match command.as_str(){ @@ -357,6 +372,36 @@ for help enter help or ?. for information about a specific command enter help (c box_controls::stop_all_boxes(&projects); } } + print_informational("saving workspace projects..."); + let mut workspace_config_path = config_path.clone(); + workspace_config_path.pop(); + let mut project_conf_path = config_path.clone(); + project_conf_path.pop(); + project_conf_path.push("projects.conf"); + if get_user_input("are your work projects currently loaded? (not yoru personal projects...)").to_lowercase().contains("y"){ + workspace_config_path.push("projects.work"); + } + else{ + workspace_config_path.push("projects.personal"); + } + let open_res = OpenOptions::new().create(true).write(true).open(workspace_config_path); + if open_res.is_err(){ + print_error("error opeing workspace config file!", open_res.err().unwrap().to_string()); + } + else{ + let mut workspace_config_file = open_res.unwrap(); + let projects_read_res = read_to_string(project_conf_path); + if projects_read_res.is_ok(){ + let project_string = projects_read_res.unwrap(); + let write_res = write!(workspace_config_file, "{}", project_string); + if write_res.is_err(){ + print_error("error writing workspace config file!", write_res.err().unwrap().to_string()); + } + } + else{ + print_error("error reading projects config file!", projects_read_res.err().unwrap().to_string()); + } + } if threads.len() > 0{ println!("closing threads..."); println!("note this will hang until all threads have completed"); diff --git a/pentest_tool/src/install.rs b/pentest_tool/src/install.rs index 1c23e96..6a4d79a 100644 --- a/pentest_tool/src/install.rs +++ b/pentest_tool/src/install.rs @@ -222,7 +222,7 @@ vault_name:{}" exit(1); } let mut project_conf_file = project_conf_res.unwrap(); - let project_write_res = write!(project_conf_file, "customer:name:notes:files:active:time:box_name:stage\ndefault:default:{}:{}:yes:{}:current", ¤t_notes.display(), ¤t_projects.display(), &template_box_name); + let project_write_res = write!(project_conf_file, "Projects Config File"); if project_write_res.is_err(){ println!("error writing project config file."); exit(1); diff --git a/pentest_tool/src/main.rs b/pentest_tool/src/main.rs index df676ec..9f62072 100644 --- a/pentest_tool/src/main.rs +++ b/pentest_tool/src/main.rs @@ -1,12 +1,9 @@ -use std::collections::btree_set::Difference; use std::collections::HashMap; use std::{io::stdin, path::PathBuf, process::Command}; -use chrono::format; use directories::UserDirs; use std::process::exit; use std::fs::{self, File}; use colored::Colorize; -use term_size::dimensions; #[derive(Clone)] pub struct Project{ @@ -128,7 +125,7 @@ pub fn tableize(given_lines: Vec) { } pub fn open_overwrite(path: &PathBuf) -> Option{ - let file_create_res = fs::OpenOptions::new().create(true).write(true).open(path); + let file_create_res = fs::OpenOptions::new().create(true).write(true).truncate(true).open(path); if file_create_res.is_err(){ let error = file_create_res.err().unwrap(); println!("{} {} {}","error opening".red(), path.display().to_string().red(), " file".red()); @@ -269,6 +266,6 @@ fn main() { return; } let projects = projects_res.unwrap(); - let _continue = get_user_input("press enter to load command line interface."); + let _continue = get_user_input("press enter to load Command Line Interface"); cli::cli(true, projects, config_path, &project_base_folder, &project_base_notes, &tools_folder, box_template, terminal_command, cracking_rig, rockyou, rule, &upcoming_files, &upcoming_notes, &pass_spray_file, fingerprint, vault_name); } diff --git a/pentest_tool/src/project_controls.rs b/pentest_tool/src/project_controls.rs index a1f90f4..befed56 100644 --- a/pentest_tool/src/project_controls.rs +++ b/pentest_tool/src/project_controls.rs @@ -1,5 +1,6 @@ use std::env; use std::fs; +use std::fs::read_to_string; use std::io::stdin; use std::io::Write; use std::path::PathBuf; @@ -11,12 +12,12 @@ use std::str::FromStr; use colored::Colorize; use crate::get_user_input; +use crate::open_overwrite; use crate::tableize; use crate::Project; use crate::box_controls::make_box; use crate::print_success; use crate::print_error; -use crate::print_informational; pub fn switch_project(projects: &mut Vec){ @@ -62,7 +63,7 @@ pub fn save_projects(projects: &Vec, config_path: &PathBuf){ return; } let mut save_file = save_file_res.unwrap(); - save_file.write_all(b"customer:name:notes:files:active:time:box_name:stage\n").expect("error writing first line to file"); + save_file.write_all(b"Current Loaded Projects Config File\n").expect("error writing first line to file"); for project in projects{ let default = format!{"{}:{}:{}:{}:", project.customer, project.project_name, project.notes_folder.display(), project.files_folder.display()}; let mut _outline = String::new(); @@ -335,37 +336,40 @@ pub fn get_projects(config_path: &PathBuf, show: bool) -> Option>{ let mut first = 0; let mut already_active = false; for line in project_lines{ + //println!("{}", line); first = first + 1; if first != 1{ if line.len() > 1{ let settings: Vec<&str> = line.split(":").collect(); - //debug config file... - /*let mut count = 0; - for settin in &settings{ - println!("{}: {}", count, settin); - count = count + 1; - }*/ - let customer = settings[0].to_owned(); - let project = settings[1].to_owned(); - let notes_string = settings[2].to_owned(); - let folder_string = settings[3].to_owned(); - let notes_folder = PathBuf::from_str(¬es_string.trim_end()).expect("error reading notes string"); - let project_folder = PathBuf::from_str(&folder_string.trim_end()).expect("error reading folding sering"); - let mut active = false; - let boxname = settings[5].to_owned(); - if settings[4] == "yes"{ - if already_active == false{ - env::set_var("CURRENT_PROJECT_BOX", boxname.clone()); - already_active = true; - active = true; + if settings.len() > 5{ + // debug config file... + /*let mut count = 0; + for settin in &settings{ + println!("{}: {}", count, settin); + count = count + 1; + }*/ + let customer = settings[0].to_owned(); + let project = settings[1].to_owned(); + let notes_string = settings[2].to_owned(); + let folder_string = settings[3].to_owned(); + let notes_folder = PathBuf::from_str(¬es_string.trim_end()).expect("error reading notes string"); + let project_folder = PathBuf::from_str(&folder_string.trim_end()).expect("error reading folding sering"); + let mut active = false; + let boxname = settings[5].to_owned(); + if settings[4] == "yes"{ + if already_active == false{ + env::set_var("CURRENT_PROJECT_BOX", boxname.clone()); + already_active = true; + active = true; + } } + let project_stage = settings[6].to_owned(); + let new_project = Project{customer: customer, project_name: project, files_folder: project_folder, notes_folder: notes_folder, active: active, id: first, boxname: boxname, stage: project_stage}; + if show{ + println!("{} {} {}", &new_project.customer, &new_project.project_name, "LOADED!".green()); + } + projects.push(new_project); } - let project_stage = settings[6].to_owned(); - let new_project = Project{customer: customer, project_name: project, files_folder: project_folder, notes_folder: notes_folder, active: active, id: first, boxname: boxname, stage: project_stage}; - if show{ - println!("{} {} {}", &new_project.customer, &new_project.project_name, "LOADED!".green()); - } - projects.push(new_project); } } } @@ -496,3 +500,132 @@ pub fn list_projects(projects: &Vec){ tableize(lines); } + +pub fn separate_personal_work_projects(config_path: &PathBuf){ + let mut projects_conf_path = config_path.clone(); + projects_conf_path.pop(); + projects_conf_path.push("projects.conf"); + let mut working_conf_path = config_path.clone(); + working_conf_path.pop(); + working_conf_path.push("projects.work"); + let mut personal_conf_path = config_path.clone(); + personal_conf_path.pop(); + personal_conf_path.push("projects.personal"); + println!("{}", projects_conf_path.display()); + let project_read_res = read_to_string(&projects_conf_path); + if project_read_res.is_err(){ + print_error("error reading current projects config file!", project_read_res.err().unwrap().to_string()); + return; + } + let project_string = project_read_res.unwrap(); + let project_lines: Vec<&str> = project_string.split("\n").collect(); + let mut personal_projects = Vec::new(); + let mut work_projects = Vec::new(); + let mut default = String::new(); + for line in project_lines{ + let words: Vec<&str> = line.split(":").collect(); + if words.len() > 3{ + if words[0].contains("default"){ + default = line.to_owned(); + } + else{ + println!("{} {}", words[0], words[1]); + if get_user_input("should this project be added to your personal projects config files?").to_lowercase().contains("y"){ + personal_projects.push(line.to_owned()); + print_success(format!("{}:{} {}",words[0], words[1], "successfully added to personal config!")); + if get_user_input("Do you also want to keep it in your work projects config?").to_lowercase().contains("y"){ + work_projects.push(line.to_owned()); + } + } + else{ + work_projects.push(line.to_owned()); + } + } + } + } + let work_config_open_res = fs::OpenOptions::new().create(true).write(true).open(working_conf_path); + if work_config_open_res.is_err(){ + print_error("error opening work projects config file!", work_config_open_res.err().unwrap().to_string()); + return; + } + let person_config_open_res = fs::OpenOptions::new().create(true).write(true).open(personal_conf_path); + if person_config_open_res.is_err(){ + print_error("error opening personal config file!", person_config_open_res.err().unwrap().to_string()); + return; + } + let mut work_config_file = work_config_open_res.unwrap(); + let mut personal_config_file = person_config_open_res.unwrap(); + let work_write_success = write!(work_config_file, "Work Config File\n"); + let personal_write_success = write!(personal_config_file, "Personal Config File\n"); + if work_write_success.is_err(){ + print_error("error writing to work config file!", work_write_success.err().unwrap().to_string()); + return; + } + else{ + work_write_success.unwrap(); + } + if personal_write_success.is_err(){ + print_error("error writing personal config file!", personal_write_success.err().unwrap().to_string()); + return; + } + else{ + personal_write_success.unwrap(); + } + write!(personal_config_file, "{}\n", default).unwrap(); + write!(work_config_file, "{}\n", default).unwrap(); + for project in work_projects{ + write!(work_config_file, "{}\n", project).unwrap(); + } + for project in personal_projects{ + write!(personal_config_file, "{}\n", project).unwrap(); + } + print_success("projects separated successfully!"); +} + +pub fn swith_to_personal(config: &PathBuf) -> Option>{ + let mut projects_path = config.clone(); + projects_path.pop(); + let mut personal_projects = config.clone(); + personal_projects.pop(); + projects_path.push("projects.conf"); + personal_projects.push("projects.personal"); + let personal_projects_read_res = read_to_string(&personal_projects); + if personal_projects_read_res.is_err(){ + print_error("error reading personal projects!", personal_projects_read_res.err().unwrap().to_string()); + return None; + } + let person_projects_string = personal_projects_read_res.unwrap(); + let open_res = open_overwrite(&projects_path); + if open_res.is_none(){ + print_error("error opening projects.conf file!", "".to_string()); + return None; + } + let mut project_conf = open_res.unwrap(); + write!(project_conf, "{}", person_projects_string).unwrap(); + let new_projects = get_projects(config, true); + return new_projects; +} + +pub fn swith_to_work(config: &PathBuf) -> Option>{ + let mut projects_path = config.clone(); + projects_path.pop(); + let mut work_projects = config.clone(); + work_projects.pop(); + projects_path.push("projects.conf"); + work_projects.push("projects.work"); + let work_projects_read_res = read_to_string(work_projects); + if work_projects_read_res.is_err(){ + print_error("error reading personal projects!", work_projects_read_res.err().unwrap().to_string()); + return None; + } + let work_projects_string = work_projects_read_res.unwrap(); + let open_res = open_overwrite(&projects_path); + if open_res.is_none(){ + print_error("", "error opening projects.conf file!".to_string()); + return None; + } + let mut project_conf = open_res.unwrap(); + write!(project_conf, "{}", work_projects_string).unwrap(); + let new_projects = get_projects(config, true); + return new_projects; +} \ No newline at end of file