use crate::SecretScanner;
use encoding::all::ASCII;
use encoding::{DecoderTrap, Encoding};
use log::{self, trace};
use s3::bucket::Bucket;
use serde_derive::{Deserialize, Serialize};
use simple_error::SimpleError;
use std::str;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, Default)]
pub struct S3Finding {
pub diff: String,
#[serde(rename = "stringsFound")]
pub strings_found: Vec<String>,
pub bucket: String,
pub key: String,
pub region: String,
pub reason: String,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct S3Scanner {
pub secret_scanner: SecretScanner,
}
impl S3Scanner {
pub fn new_from_scanner(secret_scanner: SecretScanner) -> Self {
Self { secret_scanner }
}
pub fn new() -> Self { Self { secret_scanner: SecretScanner::default() } }
pub fn scan_s3_file(
&self,
bucket: Bucket,
filepath: &str,
) -> Result<Vec<S3Finding>, SimpleError> {
let mut output: Vec<S3Finding> = Vec::new();
let (data, code) = match bucket.get_object(filepath) {
Ok(x) => (x.0, x.1),
Err(e) => return Err(SimpleError::new(e.to_string())),
};
trace!("Code: {}\nData: {:?}", code, data);
let lines = data.split(|&x| (x as char) == '\n');
for new_line in lines {
let results = self.secret_scanner.matches(new_line);
for (r, matches) in results {
let mut strings_found: Vec<String> = Vec::new();
for m in matches {
let result = ASCII
.decode(&new_line[m.start()..m.end()], DecoderTrap::Ignore)
.unwrap_or_else(|_| "<STRING DECODE ERROR>".parse().unwrap());
strings_found.push(result);
}
if !strings_found.is_empty() {
let new_line_string = ASCII
.decode(&new_line, DecoderTrap::Ignore)
.unwrap_or_else(|_| "<STRING DECODE ERROR>".parse().unwrap());
output.push(S3Finding {
diff: new_line_string,
strings_found,
bucket: bucket.name.clone(),
key: filepath.parse().unwrap(),
region: bucket.region.to_string(),
reason: r.clone(),
});
}
}
}
Ok(output)
}
}
impl Default for S3Scanner {
fn default() -> Self {
Self::new()
}
}