Skip to content
This repository has been archived by the owner on Aug 6, 2019. It is now read-only.

Commit

Permalink
Merge pull request #10 from rye/dump
Browse files Browse the repository at this point in the history
Implement Dump parsing (first pass)
  • Loading branch information
rye authored Feb 6, 2019
2 parents 7b95381 + 4c3a1de commit 2e5dbeb
Show file tree
Hide file tree
Showing 11 changed files with 259 additions and 0 deletions.
51 changes: 51 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ license = 'MIT'
[dependencies]
clap = { version = '~2.32', default-features = false }
csv = '~1.0'
serde = { version = '~1.0', features = ['derive'] }
serde_derive = '~1.0'
86 changes: 86 additions & 0 deletions src/dump.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use std::collections::HashSet;

mod detection;
mod host;
mod plugin;
mod record;

use detection::*;
use host::*;
use plugin::*;
use record::*;

#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct Dump {
pub filename: String,
pub detections: Vec<Detection>,
}

impl Dump {
pub fn read<R: std::io::Read>(rdr: R) -> Dump {
let mut reader = csv::Reader::from_reader(rdr);

let mut plugins: HashSet<Plugin> = HashSet::new();
let mut hosts: HashSet<Host> = HashSet::new();
let mut detections: HashSet<Detection> = HashSet::new();

// TODO refactor for efficiency and conciseness
reader
.deserialize()
.filter_map(|result| -> Option<Record> { result.ok() })
.for_each(|record| {
let record_plugin = Plugin {
id: record.plugin_id,
cve: vec![record.cve],
cvss: record.cvss,
risk: record.risk,
name: record.name,
synopsis: record.synopsis,
description: record.description,
solution: record.solution,
see_also: record.see_also,
};

let record_host = Host {
hostname: record.host.clone(),
addr: record.host.parse().unwrap(),
};

if let Some(plugin) = plugins.get(&record_plugin) {
if !plugin.cve.contains(record_plugin.cve.first().unwrap()) {
plugins.replace(Plugin {
cve: [plugin.cve.as_slice(), record_plugin.cve.as_slice()].concat(),
..(plugin.clone())
});
}
} else {
assert_eq!(plugins.insert(record_plugin.clone()), true);
}

let host = hosts.get(&record_host);

if host.is_none() {
assert_eq!(hosts.insert(record_host.clone()), true);
}

let plugin: Plugin = plugins.get(&record_plugin).unwrap().clone();
let host: Host = hosts.get(&record_host).unwrap().clone();

let record_detection = Detection {
host,
plugin,
plugin_output: record.plugin_output,
port: record.port,
protocol: record.protocol,
};

detections.insert(record_detection);
});

Dump {
filename: "test".to_string(),
detections: detections.iter().cloned().collect(),
}
}
}
11 changes: 11 additions & 0 deletions src/dump/detection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use super::host::*;
use super::plugin::*;

#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct Detection {
pub host: Host,
pub port: u32,
pub protocol: String,
pub plugin: Plugin,
pub plugin_output: String,
}
7 changes: 7 additions & 0 deletions src/dump/host.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use std::net::IpAddr;

#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct Host {
pub hostname: String,
pub addr: IpAddr,
}
38 changes: 38 additions & 0 deletions src/dump/plugin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

#[derive(Clone, Debug, Eq)]
pub struct Plugin {
pub id: i32,
pub cve: Vec<String>,
pub cvss: String,
pub risk: String,
pub name: String,
pub synopsis: String,
pub description: String,
pub solution: String,
pub see_also: String,
}

impl PartialEq for Plugin {
fn eq(&self, other: &Plugin) -> bool {
let mut s = DefaultHasher::new();
let mut o = DefaultHasher::new();
self.hash(&mut s);
other.hash(&mut o);
s.finish() == o.finish()
}
}

impl Hash for Plugin {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state);
self.cvss.hash(state);
self.risk.hash(state);
self.name.hash(state);
self.synopsis.hash(state);
self.description.hash(state);
self.solution.hash(state);
self.see_also.hash(state);
}
}
33 changes: 33 additions & 0 deletions src/dump/record.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// {Host, Protocol, Port} => Host
// {Plugin ID, CVE, CVSS, Name, Synopsis, Description, Solution, See Also} => Plugin
// {Plugin Output} => Detection (&Host, &Plugin) also

#[derive(Debug, Deserialize)]
pub struct Record {
#[serde(rename = "Plugin ID")]
pub plugin_id: i32,
#[serde(rename = "CVE")]
pub cve: String,
#[serde(rename = "CVSS")]
pub cvss: String,
#[serde(rename = "Risk")]
pub risk: String,
#[serde(rename = "Host")]
pub host: String,
#[serde(rename = "Protocol")]
pub protocol: String,
#[serde(rename = "Port")]
pub port: u32,
#[serde(rename = "Name")]
pub name: String,
#[serde(rename = "Synopsis")]
pub synopsis: String,
#[serde(rename = "Description")]
pub description: String,
#[serde(rename = "Solution")]
pub solution: String,
#[serde(rename = "See Also")]
pub see_also: String,
#[serde(rename = "Plugin Output")]
pub plugin_output: String,
}
9 changes: 9 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
extern crate csv;

extern crate serde;

#[macro_use]
extern crate serde_derive;

pub mod dump;
pub use dump::*;
7 changes: 7 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ extern crate csv;
extern crate clap;
use clap::{App, Arg};

use nessify::*;
use std::fs::File;

const VERSION: &str = env!("CARGO_PKG_VERSION");
const AUTHORS: &str = env!("CARGO_PKG_AUTHORS");

Expand Down Expand Up @@ -33,6 +36,10 @@ fn main() {
Some(results) => {
for dump in results {
println!("Dumps: {:?}", dump);

let file = File::open(dump).unwrap();

Dump::read(file);
}
}
None => panic!("No dump filenames given; cannot do anything."),
Expand Down
14 changes: 14 additions & 0 deletions tests/dump.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use nessify::dump::*;

#[test]
fn parses_no_records_if_schema_is_bad() {
let data = "invalid,csv
what are you,doing";

let dump: Dump = Dump::read(data.as_bytes());

dbg!(dump.clone());

assert_eq!(dump.clone().filename, "test");
assert_eq!(dump.clone().detections.len(), 0);
}
1 change: 1 addition & 0 deletions tests/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod dump;

0 comments on commit 2e5dbeb

Please sign in to comment.