use std::net::IpAddr;
use std::path::Path;
use std::time::Duration;
use serde::{Deserialize, Serialize};
use crate::Severity;
pub type ApiKeys = std::collections::HashMap<String, String>;
pub const DEFAULT_MAX_RESPONSE_SIZE: usize = 10 * 1024 * 1024;
pub const MAX_HTTP_REDIRECTS: usize = 10;
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum PortMode {
#[default]
Default,
Top100,
Top1000,
Full,
Custom(Vec<u16>),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct CrawlConfig {
pub max_pages: usize,
pub max_depth: usize,
}
impl Default for CrawlConfig {
fn default() -> Self {
Self {
max_pages: 50,
max_depth: 3,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(default)]
pub struct Config {
pub rate_limit: u32,
#[serde(default)]
pub adaptive_rate: bool,
pub timeout_secs: u64,
pub scan_timeout_secs: u64,
pub concurrency: usize,
pub resolvers: Vec<IpAddr>,
pub user_agent: String,
pub proxy: Option<String>,
#[serde(default)]
pub insecure_tls: bool,
pub cookie: Option<String>,
pub auth_user: Option<String>,
pub auth_pass: Option<String>,
#[serde(default)]
pub modules: std::collections::HashMap<String, bool>,
pub output: OutputConfig,
pub min_severity: Option<Severity>,
pub port_mode: PortMode,
pub intel_db_path: Option<String>,
#[serde(default)]
pub api_keys: ApiKeys,
#[serde(default)]
pub crawl: CrawlConfig,
#[serde(default = "default_host_delay_ms")]
pub host_delay_ms: u64,
#[serde(default = "default_max_response_size")]
pub max_response_size: usize,
#[serde(default)]
pub strict: bool,
#[serde(default)]
pub conservative: bool,
#[serde(default)]
pub include_kind: Vec<String>,
#[serde(default)]
pub exclude_kind: Vec<String>,
#[serde(default = "default_graph_json_streaming_threshold")]
pub graph_json_streaming_threshold: usize,
}
fn default_max_response_size() -> usize {
DEFAULT_MAX_RESPONSE_SIZE
}
fn default_host_delay_ms() -> u64 {
100
}
fn default_graph_json_streaming_threshold() -> usize {
10_000
}
impl Config {
pub fn scan_timeout(&self) -> Duration {
Duration::from_secs(self.scan_timeout_secs)
}
#[must_use]
pub fn timeout(&self) -> Duration {
Duration::from_secs(self.timeout_secs)
}
pub fn from_toml(path: &Path) -> Result<Self, String> {
let content = std::fs::read_to_string(path)
.map_err(|e| format!("failed to read config {}: {e}", path.display()))?;
toml::from_str(&content)
.map_err(|e| format!("failed to parse config {}: {e}", path.display()))
}
pub fn load_or_default() -> Result<Self, String> {
let path = Path::new("gossan.toml");
if path.exists() {
Self::from_toml(path)
} else {
Ok(Self::default())
}
}
}
impl Default for Config {
fn default() -> Self {
Self {
rate_limit: 300,
adaptive_rate: false,
timeout_secs: 10,
scan_timeout_secs: 600,
concurrency: 200,
resolvers: vec![
IpAddr::V4(std::net::Ipv4Addr::new(1, 1, 1, 1)),
IpAddr::V4(std::net::Ipv4Addr::new(8, 8, 8, 8)),
],
user_agent: concat!(
"gossan/",
env!("CARGO_PKG_VERSION"),
" (+https://github.com/santht/gossan)"
)
.to_string(),
proxy: None,
insecure_tls: false,
cookie: None,
auth_user: None,
auth_pass: None,
modules: std::collections::HashMap::new(),
output: OutputConfig::default(),
min_severity: None,
port_mode: PortMode::Default,
intel_db_path: None,
api_keys: ApiKeys::default(),
crawl: CrawlConfig::default(),
host_delay_ms: default_host_delay_ms(),
max_response_size: default_max_response_size(),
strict: false,
conservative: false,
include_kind: Vec::new(),
exclude_kind: Vec::new(),
graph_json_streaming_threshold: default_graph_json_streaming_threshold(),
}
}
}
pub type ModuleConfig = std::collections::HashMap<String, bool>;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum OutputFormat {
Json,
Jsonl,
Sarif,
Text,
Markdown,
MasscanGrep,
NmapXml,
Graphml,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct OutputConfig {
pub format: OutputFormat,
pub path: Option<String>,
}
impl Default for OutputConfig {
fn default() -> Self {
Self {
format: OutputFormat::Text,
path: None,
}
}
}