Skip to content

Commit 4f09a94

Browse files
authored
Merge pull request #61 from rusty-sec/change_report
Change report
2 parents a52c8ab + 04e3ca1 commit 4f09a94

File tree

10 files changed

+204
-30
lines changed

10 files changed

+204
-30
lines changed

src/cli/args.rs

+2
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,6 @@ pub struct Opts {
6868

6969
#[structopt(long = "headers", parse(try_from_str = parse_headers), required = false, default_value = "{}")]
7070
pub headers: HeaderMap,
71+
#[structopt(long = "exit-after-errors", default_value = "2000")]
72+
pub exit_after: i32,
7173
}

src/cli/bar.rs

+29
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,15 @@
1616
* limitations under the License.
1717
*/
1818

19+
use console::Style;
1920
use indicatif::{ProgressBar, ProgressStyle};
2021

22+
pub enum MessageLevel {
23+
Info,
24+
Warn,
25+
Error,
26+
}
27+
2128
/// Lotus ProgressBar based on the length of `bar` parameter
2229
pub fn create_progress(bar: u64) -> ProgressBar {
2330
let bar = ProgressBar::new(bar);
@@ -32,3 +39,25 @@ pub fn create_progress(bar: u64) -> ProgressBar {
3239
);
3340
bar
3441
}
42+
43+
pub fn show_msg(message: &str, msglevel: MessageLevel) {
44+
let print_level = match msglevel {
45+
MessageLevel::Info => {
46+
log::info!("{}", message);
47+
format!("[{}]", Style::new().blue().apply_to("INFO"))
48+
}
49+
MessageLevel::Warn => {
50+
log::warn!("{}", message);
51+
format!("[{}]", Style::new().yellow().apply_to("WARN"))
52+
}
53+
MessageLevel::Error => {
54+
log::error!("{}", message);
55+
format!("[{}]", Style::new().red().apply_to("ERROR"))
56+
}
57+
};
58+
if let MessageLevel::Error = msglevel {
59+
eprintln!("{print_level} {message}");
60+
} else {
61+
println!("{print_level} {message}");
62+
}
63+
}

src/lib.rs

+36-14
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
* limitations under the License.
1717
*/
1818

19-
#![allow(unused_imports)]
2019
mod cli;
2120
mod lua_api;
2221
mod network;
@@ -26,14 +25,15 @@ mod payloads;
2625
mod scan;
2726

2827
use cli::bar::create_progress;
28+
use cli::bar::{show_msg, MessageLevel};
2929
use cli::errors::CliErrors;
30+
use futures::{stream, StreamExt};
3031
use glob::glob;
3132
use log::error;
3233
use parsing::files::filename_to_string;
3334
use reqwest::header::HeaderMap;
3435
use std::path::PathBuf;
35-
use futures::{stream, StreamExt};
36-
use std::sync::Arc;
36+
use std::sync::{Arc, Mutex};
3737

3838
#[derive(Clone)]
3939
pub struct RequestOpts {
@@ -47,11 +47,12 @@ pub struct Lotus {
4747
pub script_path: PathBuf,
4848
pub output: Option<PathBuf>,
4949
pub workers: usize,
50-
pub script_workers: usize
50+
pub script_workers: usize,
51+
pub stop_after: Arc<Mutex<i32>>,
5152
}
5253

5354
impl Lotus {
54-
pub async fn start(&self, urls: Vec<String>, request_option: RequestOpts) {
55+
pub async fn start(&self, urls: Vec<String>, request_option: RequestOpts, exit_after: i32) {
5556
let loaded_scripts = {
5657
if self.script_path.is_dir() {
5758
self.load_scripts()
@@ -60,39 +61,60 @@ impl Lotus {
6061
}
6162
};
6263
if loaded_scripts.is_err() {
63-
eprintln!("Reading errors"); // TODO
64+
show_msg(&format!("Loading scripts error: {}",loaded_scripts.unwrap_err()), MessageLevel::Error);
6465
std::process::exit(1);
6566
}
6667
let bar =
6768
create_progress(urls.len() as u64 * loaded_scripts.as_ref().unwrap().len() as u64);
68-
if loaded_scripts.is_err() {
69-
eprintln!("Reading error bruh"); // TODO
69+
let loaded_scripts = loaded_scripts.unwrap();
70+
if self.output.is_none() {
71+
show_msg("Output argument is missing", MessageLevel::Error);
7072
std::process::exit(1);
7173
}
72-
let loaded_scripts = loaded_scripts.unwrap();
73-
7474
let lotus_obj = Arc::new(scan::LuaLoader::new(
7575
&bar,
7676
request_option.clone(),
77-
self.output.as_ref().unwrap().to_str().unwrap().to_string()));
77+
self.output.as_ref().unwrap().to_str().unwrap().to_string(),
78+
));
7879
stream::iter(urls)
7980
.map(move |url| {
8081
let loaded_scripts = loaded_scripts.clone();
8182
let lotus_loader = Arc::clone(&lotus_obj);
8283
stream::iter(loaded_scripts.into_iter())
8384
.map(move |(script_out, script_name)| {
8485
let url = url.clone();
85-
log::debug!("Running {} script on {} url",script_name, url);
8686
let lotus_loader = Arc::clone(&lotus_loader);
87+
let error_check = {
88+
if *self.stop_after.lock().unwrap() == exit_after {
89+
log::debug!("Ignoring scripts");
90+
false
91+
} else {
92+
log::debug!("Running {} script on {} url", script_name, url);
93+
true
94+
}
95+
};
8796
async move {
88-
lotus_loader.run_scan(url.as_str(),None,&script_out, &script_name).await
97+
if error_check == false {
98+
// Nothing
99+
} else {
100+
let run_scan = lotus_loader
101+
.run_scan(url.as_str(), None, &script_out, &script_name)
102+
.await;
103+
if run_scan.is_err() {
104+
log::error!("Script is raising error");
105+
let mut a = self.stop_after.lock().unwrap();
106+
log::debug!("Errors Counter: {}", a);
107+
*a += 1;
108+
}
109+
}
89110
}
90111
})
91112
.buffer_unordered(self.script_workers)
92113
.collect::<Vec<_>>()
93114
})
94115
.buffer_unordered(self.workers)
95-
.collect::<Vec<_>>().await;
116+
.collect::<Vec<_>>()
117+
.await;
96118
}
97119

98120
fn load_scripts(&self) -> Result<Vec<(String, String)>, CliErrors> {

src/lua_api.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
* limitations under the License.
1717
*/
1818

19-
use crate::output::report::{AllReports, OutReport};
19+
use crate::output::vuln::{AllReports, OutReport};
20+
use crate::output::cve::CveReport;
2021
use crate::parsing::html::{css_selector, html_parse, html_search, Location};
2122
use crate::parsing::url::HttpMessage;
2223
use crate::payloads;
@@ -93,11 +94,12 @@ pub fn http_func(target_url: &str, lua: &Lua) {
9394
.set(
9495
"Reports",
9596
AllReports {
96-
reports: Vec::new(),
97+
reports: Vec::new()
9798
},
9899
)
99100
.unwrap();
100-
lua.globals().set("NewReport", OutReport::init()).unwrap();
101+
lua.globals().set("VulnReport", OutReport::init()).unwrap();
102+
lua.globals().set("CveReport", CveReport::init()).unwrap();
101103
}
102104

103105
pub fn get_utilsfunc<'prog>(the_bar: &'prog indicatif::ProgressBar, lua: &Lua) {

src/main.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ mod cli;
2020
use cli::args::Opts;
2121
use cli::errors::CliErrors;
2222
use cli::logger::init_log;
23+
use cli::bar::{show_msg, MessageLevel};
2324
use lotus::RequestOpts;
2425
use std::io;
2526
use std::io::BufRead;
27+
use std::sync::{Arc, Mutex};
2628
use structopt::StructOpt;
2729

2830
#[tokio::main]
@@ -33,7 +35,7 @@ async fn main() -> Result<(), std::io::Error> {
3335

3436
let urls = get_target_urls();
3537
if urls.is_err() {
36-
eprintln!("EmptyStdin");
38+
show_msg("No input in Stdin",MessageLevel::Error);
3739
std::process::exit(1);
3840
}
3941
// default request options
@@ -47,7 +49,8 @@ async fn main() -> Result<(), std::io::Error> {
4749
script_path: args.script_path,
4850
output: args.output,
4951
workers: args.workers,
50-
script_workers: args.scripts_workers
52+
script_workers: args.scripts_workers,
53+
stop_after: Arc::new(Mutex::new(1)),
5154
};
5255
lotus_obj
5356
.start(
@@ -56,6 +59,7 @@ async fn main() -> Result<(), std::io::Error> {
5659
.map(|url| url.to_string())
5760
.collect::<Vec<String>>(),
5861
req_opts,
62+
args.exit_after,
5963
)
6064
.await;
6165
Ok(())

src/network/http.rs

+23
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,26 @@ impl Sender {
137137
}
138138
}
139139
}
140+
141+
pub trait SenderExt {
142+
fn make_curl(&self) -> String;
143+
}
144+
145+
impl SenderExt for Sender {
146+
fn make_curl(&self) -> String {
147+
let mut curl_command = "curl ".to_string();
148+
self.headers.iter().for_each(|(header_name, header_value)| {
149+
let header_command = format!(
150+
"-H '{}: {}'",
151+
header_name.as_str(),
152+
header_value.to_str().unwrap()
153+
);
154+
curl_command.push_str(&header_command);
155+
});
156+
if self.proxy.is_none() {
157+
curl_command.push_str(&format!("-x {}",self.proxy.clone().unwrap()));
158+
}
159+
curl_command.push_str(&format!("--connect-timeout {}",self.timeout));
160+
curl_command
161+
}
162+
}

src/output/cve.rs

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* This file is part of Lotus Project, an Web Security Scanner written in Rust based on Lua Scripts
3+
* For details, please see https://github.com/rusty-sec/lotus/
4+
*
5+
* Copyright (c) 2022 - Khaled Nassar
6+
*
7+
* Please note that this file was originally released under the
8+
* GNU General Public License as published by the Free Software Foundation;
9+
* either version 2 of the License, or (at your option) any later version.
10+
*
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
use mlua::UserData;
20+
use serde::{Deserialize, Serialize};
21+
22+
#[derive(Clone, Deserialize, Serialize)]
23+
pub struct CveReport {
24+
pub name: Option<String>,
25+
pub description: Option<String>,
26+
pub url: Option<String>,
27+
pub risk: Option<String>,
28+
pub matchers: Option<Vec<String>>,
29+
}
30+
31+
32+
impl UserData for CveReport {
33+
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
34+
methods.add_method_mut("setRisk", |_, this, risk: String| {
35+
this.risk = Some(risk);
36+
Ok(())
37+
});
38+
methods.add_method_mut("setName", |_, this, name: String| {
39+
this.name = Some(name);
40+
Ok(())
41+
});
42+
methods.add_method_mut("setUrl", |_, this, url: String| {
43+
this.url = Some(url);
44+
Ok(())
45+
});
46+
47+
methods.add_method_mut("setDescription", |_, this, description: String| {
48+
this.description = Some(description);
49+
Ok(())
50+
});
51+
52+
methods.add_method_mut("setMatchers", |_, this, matchers: Vec<String>| {
53+
this.matchers = Some(matchers);
54+
Ok(())
55+
});
56+
57+
}
58+
}
59+
60+
impl CveReport {
61+
pub fn init() -> CveReport {
62+
CveReport {
63+
name: None,
64+
description: None,
65+
url: None,
66+
risk: None,
67+
matchers: None
68+
}
69+
}
70+
}
71+

src/output/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
pub mod report;
1+
pub mod cve;
2+
pub mod vuln;

src/output/report.rs src/output/vuln.rs

+16-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@
1818

1919
use mlua::UserData;
2020
use serde::{Deserialize, Serialize};
21+
use crate::output::cve::CveReport;
22+
23+
24+
#[derive(Clone, Deserialize, Serialize)]
25+
#[serde(tag = "report_type")]
26+
pub enum LotusReport {
27+
CVE(CveReport),
28+
VULN(OutReport),
29+
}
2130

2231
#[derive(Clone, Deserialize, Serialize)]
2332
pub struct OutReport {
@@ -33,13 +42,17 @@ pub struct OutReport {
3342

3443
#[derive(Clone)]
3544
pub struct AllReports {
36-
pub reports: Vec<OutReport>,
45+
pub reports: Vec<LotusReport>,
3746
}
3847

3948
impl UserData for AllReports {
4049
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
41-
methods.add_method_mut("addReport", |_, this, the_report: OutReport| {
42-
this.reports.push(the_report);
50+
methods.add_method_mut("addVulnReport", |_, this, the_report: OutReport| {
51+
this.reports.push(LotusReport::VULN(the_report));
52+
Ok(())
53+
});
54+
methods.add_method_mut("addCveReport", |_, this, the_report: CveReport| {
55+
this.reports.push(LotusReport::CVE(the_report));
4356
Ok(())
4457
});
4558
}

0 commit comments

Comments
 (0)