From a2352dd51dc3742cac675a1a35aa76a07bf3b251 Mon Sep 17 00:00:00 2001 From: Alexander Mironov Date: Sun, 23 Jan 2022 15:52:09 +0300 Subject: [PATCH 1/7] v3.1.0 -> v3.2.0 --- Cargo.toml | 4 ++-- README.md | 4 +++- src/args.rs | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 28ed7e1..3cc8a70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x8" -version = "3.1.0" +version = "3.2.0" authors = ["Alexander Mironov "] edition = "2018" license = "GPL-3.0-or-later" @@ -14,7 +14,7 @@ readme = "README.md" [dependencies] tokio = { version = "1", features = ["full"] } futures = "0.3.15" -reqwest = { version = "0.11.2", features = ["socks", "json", "cookies", "rustls-tls", "trust-dns", "gzip"] } +reqwest = { version = "0.11", features = ["socks", "json", "cookies", "rustls-tls", "trust-dns", "gzip"] } regex = "1.3.7" percent-encoding = "2.1.0" lazy_static = "1.4.0" diff --git a/README.md b/README.md index d2d4d46..a762858 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,7 @@ USAGE: x8 [FLAGS] [OPTIONS] FLAGS: + --append Append to the output file instead of overwriting it. --as-body Send parameters via body. Built in body types that can be detected automatically: json, urlencode --disable-cachebuster @@ -152,6 +153,7 @@ FLAGS: keyword - specify this argument for a more accurate search --keep-newlines --body 'a\r\nb' -> --body 'a{{new_line}}b'. Works with body and parameter templates only. + --reflected-only Disable page comparison and search for reflected parameters only. --replay-once If replay proxy is specified, send all found parameters within one request. --test Prints request and response -V, --version Prints version information @@ -193,7 +195,7 @@ OPTIONS: --save-responses Save matched responses to a directory -u, --url You can add a custom injection point with %s. --value-size - Custom value size. Affects {{random}} variables as well (default is 5) + Custom value size. Affects {{random}} variables as well (default is 7) -v, --verbose Verbose level 0/1/2 (default is 1) -w, --wordlist The file with parameters diff --git a/src/args.rs b/src/args.rs index c1d5173..77a5fd2 100644 --- a/src/args.rs +++ b/src/args.rs @@ -90,7 +90,7 @@ pub fn get_config() -> (Config, usize) { .arg( Arg::with_name("append") .long("append") - .help("Append to the output file instead of overwriting it") + .help("Append to the output file instead of overwriting it.") ) .arg( Arg::with_name("method") From 1fea95ece2765f186e98202edbd2bd401c81ce6e Mon Sep 17 00:00:00 2001 From: Alexander Mironov Date: Sun, 23 Jan 2022 16:23:23 +0300 Subject: [PATCH 2/7] Add new installation method for linux --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index a762858..6700fba 100644 --- a/README.md +++ b/README.md @@ -260,6 +260,10 @@ In the next dialog, you can change the command and run it in a new terminal wind - Linux - from releases + - from blackarch repositories (repositories should be installed) + ```bash + # pacman -Sy x8 + ``` - from source code (rust should be installed) ```bash git clone https://github.com/Sh1Yo/x8 From 398819dbf788636a80577a75e6413d9a014d9004 Mon Sep 17 00:00:00 2001 From: Alexander Mironov Date: Mon, 31 Jan 2022 22:41:52 +0300 Subject: [PATCH 3/7] Fix detection of reflected parameters (thanks @HolyBugx for this one) --- Cargo.toml | 2 +- src/requests.rs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3cc8a70..5b3f139 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x8" -version = "3.2.0" +version = "3.2.1" authors = ["Alexander Mironov "] edition = "2018" license = "GPL-3.0-or-later" diff --git a/src/requests.rs b/src/requests.rs index 9905a9e..ad299fe 100644 --- a/src/requests.rs +++ b/src/requests.rs @@ -297,14 +297,6 @@ pub async fn request( Err(_) => String::new(), }; - let mut reflected_params: Vec = Vec::new(); - - for (key, value) in initial_query.iter() { - if value.contains("%random%_") && body.to_ascii_lowercase().matches(&value.replace("%random%_", "").as_str()).count() as usize != reflections { - reflected_params.push(key.to_string()); - } - } - let mut text = String::new(); for (key, value) in headers.iter() { text.push_str(&key); @@ -315,6 +307,14 @@ pub async fn request( text.push_str(&"\n\n"); text.push_str(&body); + let mut reflected_params: Vec = Vec::new(); + + for (key, value) in initial_query.iter() { + if value.contains("%random%_") && text.to_ascii_lowercase().matches(&value.replace("%random%_", "").as_str()).count() as usize != reflections { + reflected_params.push(key.to_string()); + } + } + ResponseData { text, code, From ebe61d2fc1a6338c76626a6e2d3a0436cdaaef82 Mon Sep 17 00:00:00 2001 From: Alexander Mironov Date: Thu, 10 Feb 2022 21:25:18 +0300 Subject: [PATCH 4/7] Clean code --- src/args.rs | 128 ++++++++++-------------------------------------- src/logic.rs | 20 +++++--- src/main.rs | 51 +++++++++---------- src/requests.rs | 51 +++++++++---------- src/structs.rs | 8 +-- src/utils.rs | 7 ++- 6 files changed, 96 insertions(+), 169 deletions(-) diff --git a/src/args.rs b/src/args.rs index 77a5fd2..784d5f2 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,6 +1,6 @@ use crate::{structs::Config, utils::{parse_request, adjust_body}}; use clap::{crate_version, App, AppSettings, Arg}; -use std::{collections::HashMap, fs, time::Duration, io::{self, Write}}; +use std::{collections::HashMap, fs, time::Duration}; use url::Url; pub fn get_config() -> (Config, usize) { @@ -259,25 +259,12 @@ pub fn get_config() -> (Config, usize) { let args = app.clone().get_matches(); - let delay = match args.value_of("delay") { - Some(val) => match val.parse() { - Ok(val) => Duration::from_millis(val), - Err(_) => { - writeln!(io::stderr(), "Unable to parse 'delay' value").ok(); - std::process::exit(1); - } - }, - None => Duration::from_millis(0), - }; + let delay = Duration::from_millis( + args.value_of("delay").unwrap_or("0").parse().expect("Unable to parse 'delay' value") + ); let max: usize = match args.value_of("max") { - Some(val) => match val.parse() { - Ok(val) => val, - Err(_) => { - writeln!(io::stderr(), "Unable to parse 'max' value").ok(); - std::process::exit(1); - } - }, + Some(val) => val.parse().expect("Unable to parse 'max' value"), None => { if args.is_present("as-body") { 512 @@ -289,65 +276,22 @@ pub fn get_config() -> (Config, usize) { } }; - let value_size: usize = match args.value_of("value_size") { - Some(val) => match val.parse() { - Ok(val) => val, - Err(_) => { - writeln!(io::stderr(), "Unable to parse 'value_size' value").ok(); - std::process::exit(1); - } - }, - None => { - 7 - } - }; + let value_size: usize = args.value_of("value_size").unwrap_or("7") + .parse().expect("Unable to parse 'value_size' value"); - let learn_requests_count: usize = match args.value_of("learn_requests_count") { - Some(val) => match val.parse() { - Ok(val) => val, - Err(_) => { - writeln!(io::stderr(), "Unable to parse 'learn_requests_count' value").ok(); - std::process::exit(1); - } - }, - None => { - 9 - } - }; + let learn_requests_count: usize = args.value_of("learn_requests_count").unwrap_or("9") + .parse().expect("Unable to parse 'learn_requests_count' value"); - let concurrency: usize = match args.value_of("concurrency") { - Some(val) => match val.parse() { - Ok(val) => val, - Err(_) => { - writeln!(io::stderr(), "Unable to parse 'concurrency' value").ok(); - std::process::exit(1); - } - }, - None => { - 1 - } - }; + let concurrency: usize = args.value_of("concurrency").unwrap_or("1").parse().expect( "Unable to parse 'concurrency' value"); let mut headers: HashMap = HashMap::new(); let mut within_headers: bool = false; if let Some(val) = args.values_of("headers") { for header in val { let mut k_v = header.split(':'); - let key = match k_v.next() { - Some(val) => val, - None => { - writeln!(io::stderr(), "Unable to parse headers").ok(); - std::process::exit(1); - } - }; + let key = k_v.next().expect("Unable to parse headers"); let value: String = [ - match k_v.next() { - Some(val) => val.trim().to_owned(), - None => { - writeln!(io::stderr(), "Unable to parse headers").ok(); - std::process::exit(1); - } - }, + k_v.next().expect("Unable to parse headers").trim().to_owned(), k_v.map(|x| ":".to_owned() + x).collect(), ].concat(); @@ -359,18 +303,9 @@ pub fn get_config() -> (Config, usize) { } }; - let verbose: u8 = match args.value_of("verbose") { - Some(val) => val.parse().expect("incorrect verbose"), - None => 1, - }; + let verbose: u8 = args.value_of("verbose").unwrap_or("1").parse().expect("incorrect verbose"); - let url = match Url::parse(args.value_of("url").unwrap_or("https://example.com")) { - Ok(val) => val, - Err(err) => { - writeln!(io::stderr(), "Unable to parse target url: {}", err).ok(); - std::process::exit(1); - }, - }; + let url = Url::parse(args.value_of("url").unwrap()).expect("Unable to parse target url"); let host = url.host_str().unwrap(); let mut path = url[url::Position::BeforePath..].to_string(); @@ -441,7 +376,7 @@ pub fn get_config() -> (Config, usize) { let mut url = args .value_of("url") - .unwrap_or("https://something.something") + .unwrap() .to_string(); if !args.is_present("as-body") && !within_headers && !args.is_present("headers-discovery") && url.contains('?') && url.contains('=') && !url.contains("%s") { @@ -468,13 +403,6 @@ pub fn get_config() -> (Config, usize) { }; let mut parameter_template = parameter_template.as_str(); - if !parameter_template.is_empty() - && (!parameter_template.contains("%k") || !parameter_template.contains("%v")) - && !args.is_present("force") { - writeln!(io::stderr(), "param_template lacks important variables like %k or %v").ok(); - std::process::exit(1); - } - if parameter_template.is_empty() { if body_type.contains("json") && args.is_present("as-body") { parameter_template = "\"%k\":\"%v\", "; @@ -515,15 +443,10 @@ pub fn get_config() -> (Config, usize) { } - let request = match args.value_of("request") { - Some(val) => match fs::read_to_string(val) { - Ok(val) => val, - Err(err) => { - writeln!(io::stderr(), "Unable to open request file: {}", err).ok(); - std::process::exit(1); - } - }, - None => String::new(), + let request = if args.value_of("request").is_some() { + fs::read_to_string(args.value_of("request").unwrap()).expect("Unable to open request file") + } else { + String::new() }; if args.is_present("disable-colors") { @@ -573,13 +496,12 @@ pub fn get_config() -> (Config, usize) { }; config = if !request.is_empty() { - match parse_request(config, args.value_of("proto").unwrap_or("https"), &request, !args.value_of("parameter_template").unwrap_or("").is_empty()) { - Some(val) => val, - None => { - writeln!(io::stderr(), "Unable to parse request file.").ok(); - std::process::exit(1); - } - } + parse_request( + config, + args.value_of("proto").unwrap_or("https"), + &request, + !args.value_of("parameter_template").unwrap_or("").is_empty() + ).expect("Unable to parse request file") } else { config }; diff --git a/src/logic.rs b/src/logic.rs index 1cd6ff3..2890766 100644 --- a/src/logic.rs +++ b/src/logic.rs @@ -1,6 +1,6 @@ use crate::{ requests::{random_request, request}, - structs::{Config, ResponseData, Stable, FuturesData, Statistic}, + structs::{Config, ResponseData, DefaultResponse, Stable, FuturesData, Statistic}, utils::{compare, make_hashmap, random_line, generate_request}, }; use colored::*; @@ -50,10 +50,13 @@ pub async fn check_parameters( async move { let query = &make_hashmap(&chunk, config.value_size); - let response = request(config, &mut futures_data.stats, client, query, reflections_count).await; + let response = + request(config, &mut futures_data.stats, client, query, reflections_count) + .await + .unwrap_or(ResponseData::default()); //progress bar - if config.verbose > 0 && !config.disable_progress_bar { + if config.verbose > 0 && !config.disable_progress_bar { //TODO maybe use external library write!( io::stdout(), "{} {}/{} \r", @@ -132,7 +135,9 @@ pub async fn check_parameters( drop(diffs); let tmp_resp = - random_request(&config, &mut futures_data.stats, &client, reflections_count, max).await; + random_request(&config, &mut futures_data.stats, &client, reflections_count, max) + .await + .unwrap_or(ResponseData::default()); //lock it again diffs = cloned_diffs.lock(); @@ -289,7 +294,10 @@ pub async fn check_parameters( config.value_size, ); - let check_response = request(config, &mut futures_data.stats, client, &query, 0).await; + let check_response = + request(config, &mut futures_data.stats, client, &query, 0) + .await + .unwrap_or(ResponseData::default()); if check_response.code != initial_response.code { writeln!( @@ -297,7 +305,7 @@ pub async fn check_parameters( "[!] {} the page became unstable (code)", &config.url ).ok(); - std::process::exit(1) + std::process::exit(1) //TODO return error instead } else { let mut green_lines = cloned_green_lines.lock(); green_lines.insert(response.code.to_string(), 0); diff --git a/src/main.rs b/src/main.rs index 2300454..65ecd93 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,7 @@ use x8::{ args::get_config, logic::check_parameters, requests::{empty_reqs, random_request, request}, - structs::{Config, Statistic}, + structs::{Config, Statistic, ResponseData, DefaultResponse}, utils::{compare, generate_data, heuristic, make_hashmap, random_line, read_lines, create_output}, }; @@ -56,18 +56,7 @@ async fn run() { } if !config.save_responses.is_empty() { - match fs::create_dir(&config.save_responses) { - Ok(_) => (), - Err(err) => { - writeln!( - io::stderr(), - "Unable to create a directory '{}' due to {}", - &config.save_responses, - err - ).unwrap_or(()); - std::process::exit(1); - } - }; + fs::create_dir(&config.save_responses).expect("Unable to create a directory"); } let mut params: Vec = Vec::new(); @@ -130,17 +119,14 @@ async fn run() { // if opened in the test mode - generate request/response and quit if config.test { generate_data(&config, &mut stats, &client, &query).await; - std::process::exit(0) + return } // make first request and collect some information like code, reflections, possible parameters - let mut initial_response = request(&config, &mut stats, &client, &query, 0).await; - - if initial_response.code == 0 { - writeln!(io::stderr(), "Unable to reach - {} ", &config.url).ok(); - std::process::exit(1) - } - + let mut initial_response = + request(&config, &mut stats, &client, &query, 0) + .await + .expect("Unable to connect to the server"); if !config.headers_discovery { for param in heuristic(&initial_response.text) { @@ -195,12 +181,15 @@ async fn run() { if config.reflected_only && !stable.reflections { writeln!(io::stderr(), "{} Reflections are not stable", config.url).ok(); - std::process::exit(1); + return } //check whether it is possible to use 192(128) or 256(196) params in a single request instead of 128 default if max == 128 || max == 64 { - let response = random_request(&config, &mut stats, &client, reflections_count, max + 64).await; + let response = + random_request(&config, &mut stats, &client, reflections_count, max + 64) + .await + .expect("The page is not stable"); let (is_code_the_same, new_diffs) = compare(&initial_response, &response); let mut is_the_body_the_same = true; @@ -212,7 +201,11 @@ async fn run() { } if is_code_the_same && (!stable.body || is_the_body_the_same) { - let response = random_request(&config, &mut stats, &client, reflections_count, max + 128).await; + let response = + random_request(&config, &mut stats, &client, reflections_count, max + 128) + .await + .expect("The page is not stable"); + let (is_code_the_same, new_diffs) = compare(&initial_response, &response); for diff in new_diffs { @@ -268,7 +261,7 @@ async fn run() { || (count > 1 && remaining_params.len() > (initial_size * 2 + 10)) { writeln!(io::stderr(), "{} Infinity loop detected", config.url).ok(); - std::process::exit(1); + return } params = Vec::with_capacity(remaining_params.len() * max); @@ -326,6 +319,7 @@ async fn run() { if config.verify { let mut filtered_params = HashMap::with_capacity(found_params.len()); for (param, reason) in found_params { + let response = request( &config, &mut stats, @@ -334,14 +328,17 @@ async fn run() { &[param.clone()], config.value_size ), reflections_count - ).await; + ).await.unwrap_or(ResponseData::default()); + let (is_code_the_same, new_diffs) = compare(&initial_response, &response); let mut is_the_body_the_same = true; + for diff in new_diffs.iter() { if !diffs.iter().any(|i| &i==&diff) { is_the_body_the_same = false; } } + if !response.reflected_params.is_empty() || !is_the_body_the_same || !is_code_the_same { filtered_params.insert(param, reason); } @@ -406,7 +403,7 @@ async fn run() { Err(err) => { writeln!(io::stderr(), "[!] Unable to create file - {}", err).ok(); write!(io::stdout(), "\n{}", &output).ok(); - std::process::exit(1); + return } } }; diff --git a/src/requests.rs b/src/requests.rs index ad299fe..c80ddb7 100644 --- a/src/requests.rs +++ b/src/requests.rs @@ -1,5 +1,5 @@ use crate::{ - structs::{Config, ResponseData, Stable, Statistic}, + structs::{Config, ResponseData, Stable, Statistic, DefaultResponse}, utils::{compare, beautify_html, beautify_json, make_body, make_query, make_header_value, make_hashmap, fix_headers, random_line}, }; use colored::*; @@ -11,6 +11,8 @@ use std::{ io::{self, Write}, }; +const MAX_PAGE_SIZE: usize = 25 * 1024 * 1024; //25MB usually + //makes first requests and checks page behavior pub async fn empty_reqs( config: &Config, @@ -28,7 +30,10 @@ pub async fn empty_reqs( let mut diffs: Vec = Vec::new(); for i in 0..count { - let response = random_request(config, stats, client, reflections_count, max).await; + let response = + random_request(config, stats, client, reflections_count, max) + .await + .unwrap_or(ResponseData::default()); //progress bar if config.verbose > 0 && !config.disable_progress_bar { @@ -42,8 +47,8 @@ pub async fn empty_reqs( io::stdout().flush().unwrap_or(()); } - if response.text.len() > 25 * 1024 * 1024 && !config.force { - writeln!(io::stderr(), "[!] {} the page is too huge", &config.url).ok(); + if response.text.len() > MAX_PAGE_SIZE && !config.force { + writeln!(io::stderr(), "[!] {} the page is too huge", &config.url).ok(); //TODO return error std::process::exit(1) } @@ -69,7 +74,10 @@ pub async fn empty_reqs( } } - let response = random_request(config, stats, client, reflections_count, max).await; + let response = + random_request(config, stats, client, reflections_count, max) + .await + .unwrap_or(ResponseData::default());//TODO replace with for diff in compare(initial_response, &response).1 { if !diffs.iter().any(|i| i == &diff) { @@ -94,7 +102,7 @@ pub async fn random_request( client: &Client, reflections: usize, max: usize, -) -> ResponseData { +) -> Option { request( &config, stats, @@ -129,7 +137,7 @@ fn create_request( "HEAD" => client.head(url).body(query.clone()), _ => { writeln!(io::stderr(), "Method is not supported").ok(); - std::process::exit(1); + std::process::exit(1); //TODO return error }, } } else { @@ -186,7 +194,7 @@ pub async fn request( client: &Client, initial_query: &HashMap, reflections: usize, -) -> ResponseData { +) -> Option { let mut hashmap_query: HashMap = HashMap::with_capacity(initial_query.len()); for (k, v) in initial_query.iter() { hashmap_query.insert(k.to_string(), v.replace("%random%_", "")); @@ -236,33 +244,22 @@ pub async fn request( stats.amount_of_requests += 1; match create_request(config, random_query.clone(), &hashmap_query, client).send().await { - Ok(_) => return ResponseData { - text: String::new(), - code: 0, - reflected_params: Vec::new(), - }, + Ok(_) => return None, Err(err) => { writeln!(io::stderr(), "[!] {} {:?}", url, err).ok(); match err.source() { Some(val) => if val.to_string() == "invalid HTTP version parsed" && !config.http2 { writeln!(io::stdout(), "[!] {}", "Try to use --http2 option".bright_red()).ok(); - std::process::exit(1); + return None }, None => () }; writeln!(io::stderr(), "[~] error at the {} observed. Wait 50 sec and repeat.", config.url).ok(); std::thread::sleep(Duration::from_secs(50)); - match create_request(config, random_query, &hashmap_query, client).send().await { - Ok(_) => return ResponseData { - text: String::new(), - code: 0, - reflected_params: Vec::new(), - }, - Err(_) => { - writeln!(io::stderr(), "[!] unable to reach {}", config.url).ok(); - std::process::exit(1); - } - } + create_request(config, random_query, &hashmap_query, client) + .send() + .await + .expect("Unable to connect to the server") //TODO return error instead } } } @@ -315,9 +312,9 @@ pub async fn request( } } - ResponseData { + Some(ResponseData { text, code, reflected_params, - } + }) } \ No newline at end of file diff --git a/src/structs.rs b/src/structs.rs index a92726e..8a66d29 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -1,8 +1,8 @@ use std::{collections::HashMap, time::Duration}; -/*pub trait DefaultResponse { +pub trait DefaultResponse { fn default() -> ResponseData; -}*/ +} #[derive(Debug)] pub struct ResponseData { @@ -11,7 +11,7 @@ pub struct ResponseData { pub reflected_params: Vec, } -/*impl DefaultResponse for ResponseData { +impl DefaultResponse for ResponseData { fn default() -> ResponseData { ResponseData { text: String::new(), @@ -19,7 +19,7 @@ pub struct ResponseData { reflected_params: vec![], } } -}*/ +} #[derive(Debug, Clone)] pub struct FuturesData { diff --git a/src/utils.rs b/src/utils.rs index a2e5529..e73bad0 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -59,7 +59,7 @@ pub fn compare( ) { Ok(val) => val, Err(err) => { - writeln!(io::stderr(), "Unable to compare: {}", err).ok(); + writeln!(io::stderr(), "Unable to compare: {}", err).ok(); //TODO return error instead std::process::exit(1); } } { @@ -196,7 +196,10 @@ pub async fn generate_data(config: &Config, stats: &mut Statistic, client: &Clie writeln!(io::stdout(), "Request:\n{}", req).ok(); - let response = request(config, stats, client, &query, 0).await; + let response = + request(config, stats, client, &query, 0) + .await + .expect("Unable to connect to the server"); writeln!( io::stdout(), From ba986ff783c8e527062ebed741fa571e3dedc3f3 Mon Sep 17 00:00:00 2001 From: Alexander Mironov Date: Thu, 10 Feb 2022 21:28:03 +0300 Subject: [PATCH 5/7] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 6700fba..6787dfa 100644 --- a/README.md +++ b/README.md @@ -229,6 +229,9 @@ It is possible to run parameter discovery in a few clicks using burp suite exten ## [x8-Burp](https://github.com/Impact-I/x8-Burp) ![preview](https://user-images.githubusercontent.com/54232788/126073100-ed09e8b1-0ffa-4432-aa34-f0451586a992.png) +### NOTE +Currently the extension supports only v2.5.0. + ## [Send To](https://portswigger.net/bappstore/f089f1ad056545489139cb9f32900f8e) ### Setting up From 5f3b3cdf66c7f1284530c5916edb83ad3817fba6 Mon Sep 17 00:00:00 2001 From: Alexander Mironov Date: Mon, 7 Mar 2022 20:12:42 +0300 Subject: [PATCH 6/7] Change donation method --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6787dfa..b515686 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ [![Twitter](https://img.shields.io/twitter/follow/sh1yo_.svg?logo=twitter)](https://twitter.com/sh1yo_) -[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/B0B858X5E) - ![crates.io](https://img.shields.io/crates/v/x8.svg) ![stars](https://img.shields.io/github/stars/Sh1Yo/x8) ![crates_downloads](https://img.shields.io/crates/d/x8?logo=rust) @@ -292,3 +290,7 @@ In the next dialog, you can change the command and run it in a new terminal wind - Windows - from releases + +-- +btc - bc1qje9f85652r5a0anfxcs8yzu97nes740qxg3mxt4um30myj5sc7mss0v3yw +xmr - 46pni5AY9Ra399sivBykVucaK6KdU3rYiSqFsZinfaEgd3qUkeZvRxjEdhPPmsmZQwTDPBSrvSpkaj4LsHqLH6GG7zMmgiW \ No newline at end of file From ec27d570adbba9c31f67fafe8d3da13fccfd6289 Mon Sep 17 00:00:00 2001 From: Alexander Mironov Date: Wed, 13 Apr 2022 17:00:45 +0300 Subject: [PATCH 7/7] Add some code improvements --- Cargo.toml | 1 + src/args.rs | 90 +++++++++++++++++++++++++++++--------------------- src/structs.rs | 2 +- 3 files changed, 55 insertions(+), 38 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5b3f139..a43ca43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,3 +24,4 @@ colored = "2" diffs = "0.2.1" url = "2.1.1" parking_lot = "0.11" +log = "0.4.14" diff --git a/src/args.rs b/src/args.rs index 784d5f2..ebdb72f 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,6 +1,7 @@ use crate::{structs::Config, utils::{parse_request, adjust_body}}; use clap::{crate_version, App, AppSettings, Arg}; use std::{collections::HashMap, fs, time::Duration}; +use log; use url::Url; pub fn get_config() -> (Config, usize) { @@ -43,6 +44,7 @@ pub fn get_config() -> (Config, usize) { .short("P") .long("param-template") .help("%k - key, %v - value. Example: --param-template 'user[%k]=%v&'") + .default_value("") .takes_value(true), ) .arg( @@ -57,7 +59,8 @@ pub fn get_config() -> (Config, usize) { Arg::with_name("body-type") .short("t") .long("body-type") - .help("Available: urlencode, json. (default is \"urlencode\")\nCan be detected automatically if --body is specified") + .help("Available: urlencode, json\nCan be detected automatically if --body is specified") + .default_value("urlencode") .value_name("body type") ) .arg( @@ -71,6 +74,7 @@ pub fn get_config() -> (Config, usize) { .short("d") .long("delay") .value_name("Delay between requests in milliseconds") + .default_value("0") .takes_value(true) ) .arg( @@ -84,7 +88,8 @@ pub fn get_config() -> (Config, usize) { Arg::with_name("output-format") .short("O") .long("output-format") - .help("standart, json, url, request (default is \"standart\")") + .help("standart, json, url, request") + .default_value("standart") .takes_value(true) ) .arg( @@ -97,7 +102,8 @@ pub fn get_config() -> (Config, usize) { .short("X") .long("method") .value_name("method") - .help("Available: GET, POST, PUT, PATCH, DELETE, HEAD. (default is \"GET\")") + .help("Available: GET, POST, PUT, PATCH, DELETE, HEAD.") + .default_value("GET") .takes_value(true) .conflicts_with("request") ) @@ -202,7 +208,8 @@ pub fn get_config() -> (Config, usize) { Arg::with_name("verbose") .long("verbose") .short("v") - .help("Verbose level 0/1/2 (default is 1)") + .help("Verbose level 0/1/2") + .default_value("1") .takes_value(true) ) .arg( @@ -218,13 +225,15 @@ pub fn get_config() -> (Config, usize) { .arg( Arg::with_name("value_size") .long("value-size") - .help("Custom value size. Affects {{random}} variables as well (default is 7)") + .help("Custom value size. Affects {{random}} variables as well") + .default_value("7") .takes_value(true) ) .arg( Arg::with_name("learn_requests_count") .long("learn-requests") - .help("Set the custom number of learning requests. (default is 9)") + .help("Set the custom number of learning requests.") + .default_value("9") .takes_value(true) ) .arg( @@ -237,7 +246,8 @@ pub fn get_config() -> (Config, usize) { .arg( Arg::with_name("concurrency") .short("c") - .help("The number of concurrent requests (default is 1)") + .help("The number of concurrent requests") + .default_value("1") .takes_value(true) ) .arg( @@ -259,30 +269,24 @@ pub fn get_config() -> (Config, usize) { let args = app.clone().get_matches(); - let delay = Duration::from_millis( - args.value_of("delay").unwrap_or("0").parse().expect("Unable to parse 'delay' value") - ); + let delay = Duration::from_millis(parse_int(&args, "delay") as u64); - let max: usize = match args.value_of("max") { - Some(val) => val.parse().expect("Unable to parse 'max' value"), - None => { - if args.is_present("as-body") { - 512 - } else if !args.is_present("headers-discovery") { - 128 - } else { - 64 - } + let max: usize = if args.is_present("max") { + parse_int(&args, "max") + } else { + if args.is_present("as-body") { + 512 + } else if !args.is_present("headers-discovery") { + 128 + } else { + 64 } }; - let value_size: usize = args.value_of("value_size").unwrap_or("7") - .parse().expect("Unable to parse 'value_size' value"); - - let learn_requests_count: usize = args.value_of("learn_requests_count").unwrap_or("9") - .parse().expect("Unable to parse 'learn_requests_count' value"); - - let concurrency: usize = args.value_of("concurrency").unwrap_or("1").parse().expect( "Unable to parse 'concurrency' value"); + let value_size = parse_int(&args, "value_size"); + let learn_requests_count = parse_int(&args, "learn_requests_count"); + let concurrency = parse_int(&args, "concurrency"); + let verbose = parse_int(&args, "verbose"); let mut headers: HashMap = HashMap::new(); let mut within_headers: bool = false; @@ -303,21 +307,26 @@ pub fn get_config() -> (Config, usize) { } }; - let verbose: u8 = args.value_of("verbose").unwrap_or("1").parse().expect("incorrect verbose"); - - let url = Url::parse(args.value_of("url").unwrap()).expect("Unable to parse target url"); + //default value is used only in case a request file is used. Then it gets overwrited in parse_request() + let url = match Url::parse(args.value_of("url").unwrap_or("https://4rt.one")) { + Ok(val) => val, + Err(err) => { + log::error!("Unable to parse target url: {}", err); + std::process::exit(1); + } + }; let host = url.host_str().unwrap(); let mut path = url[url::Position::BeforePath..].to_string(); let body = match args.is_present("keep-newlines") { - true => args.value_of("body").unwrap_or("")/*.replace("\\\\", "\\")*/.replace("\\n", "\n").replace("\\r", "\r"), + true => args.value_of("body").unwrap_or("").replace("\\n", "\n").replace("\\r", "\r"), false => args.value_of("body").unwrap_or("").to_string() }; //check whether it is possible to automatically fix body type //- at the end means "specified automatically" - let body_type = if args.value_of("body-type").is_none() && args.value_of("parameter_template").unwrap_or("").is_empty() + let body_type = if args.value_of("body-type").is_none() && args.value_of("parameter_template").unwrap().is_empty() && ( ( !body.is_empty() && body.starts_with('{') @@ -374,10 +383,7 @@ pub fn get_config() -> (Config, usize) { } } - let mut url = args - .value_of("url") - .unwrap() - .to_string(); + let mut url = url.to_string(); if !args.is_present("as-body") && !within_headers && !args.is_present("headers-discovery") && url.contains('?') && url.contains('=') && !url.contains("%s") { if args.is_present("encode") { @@ -454,7 +460,7 @@ pub fn get_config() -> (Config, usize) { } let mut config = Config { - method: args.value_of("method").unwrap_or("GET").to_string(), + method: args.value_of("method").unwrap().to_string(), initial_url: args.value_of("url").unwrap_or("").to_string(), url, host: host.to_string(), @@ -507,4 +513,14 @@ pub fn get_config() -> (Config, usize) { }; (config, max) +} + +fn parse_int(args: &clap::ArgMatches, value: &str) -> usize { + match args.value_of(value).unwrap().parse() { + Ok(val) => val, + Err(err) => { + log::error!("Unable to parse '{}' value: {}", value, err); + std::process::exit(1); + } + } } \ No newline at end of file diff --git a/src/structs.rs b/src/structs.rs index 8a66d29..c8899d6 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -58,7 +58,7 @@ pub struct Config { pub as_body: bool, pub headers_discovery: bool, pub within_headers: bool, - pub verbose: u8, + pub verbose: usize, pub is_json: bool, pub disable_cachebuster: bool, pub delay: Duration,