use super::error::FindingBuildError;
use super::types::FindingConfig;
fn validate_non_empty_field(
value: &str,
error: FindingBuildError,
) -> Result<(), FindingBuildError> {
if value.is_empty() {
return Err(error);
}
Ok(())
}
fn validate_max_len(value: &str, field: &'static str, max: usize) -> Result<(), FindingBuildError> {
if value.len() > max {
return Err(FindingBuildError::FieldTooLong { field, max });
}
Ok(())
}
fn validate_string_content(value: &str, field: &'static str) -> Result<(), FindingBuildError> {
if value.contains('\0') {
return Err(FindingBuildError::InvalidField {
field,
reason: "cannot contain null bytes",
});
}
if value.contains('\u{FFFD}') {
return Err(FindingBuildError::InvalidField {
field,
reason: "cannot contain Unicode replacement character (U+FFFD)",
});
}
Ok(())
}
pub(crate) fn validate_scanner(
scanner: &str,
config: &FindingConfig,
) -> Result<(), FindingBuildError> {
validate_non_empty_field(scanner, FindingBuildError::EmptyScanner)?;
validate_max_len(scanner, "scanner", config.max_scanner_len)?;
validate_string_content(scanner, "scanner")
}
pub(crate) fn validate_target(
target: &str,
config: &FindingConfig,
) -> Result<(), FindingBuildError> {
validate_non_empty_field(target, FindingBuildError::EmptyTarget)?;
validate_max_len(target, "target", config.max_target_len)?;
validate_string_content(target, "target")
}
pub(crate) fn validate_title(title: &str, config: &FindingConfig) -> Result<(), FindingBuildError> {
validate_non_empty_field(title, FindingBuildError::EmptyTitle)?;
validate_max_len(title, "title", config.max_title_len)?;
validate_string_content(title, "title")
}
pub(crate) fn validate_detail(
detail: &str,
config: &FindingConfig,
) -> Result<(), FindingBuildError> {
validate_max_len(detail, "detail", config.max_detail_len)?;
validate_string_content(detail, "detail")
}
pub(crate) fn validate_cve(cve: &str) -> Result<(), FindingBuildError> {
if !cve.starts_with("CVE-") || cve.len() > 30 || cve.len() < 8 {
return Err(FindingBuildError::InvalidCveFormat(cve.to_string()));
}
validate_string_content(cve, "cve_ids")
}
pub(crate) fn validate_cwe(cwe: &str) -> Result<(), FindingBuildError> {
if !cwe.starts_with("CWE-") || cwe.len() > 30 || cwe.len() < 5 {
return Err(FindingBuildError::InvalidCweFormat(cwe.to_string()));
}
validate_string_content(cwe, "cwe_ids")
}
pub(crate) fn validate_confidence(
confidence: Option<f64>,
) -> Result<Option<f64>, FindingBuildError> {
match confidence {
Some(conf) if !conf.is_finite() => Err(FindingBuildError::InvalidConfidence),
Some(conf) if !(0.0..=1.0).contains(&conf) => Err(FindingBuildError::InvalidConfidence),
Some(conf) => Ok(Some(conf)),
None => Ok(None),
}
}
pub(crate) fn validate_cvss_score(
cvss_score: Option<f64>,
) -> Result<Option<f64>, FindingBuildError> {
match cvss_score {
Some(score) if !score.is_finite() => Err(FindingBuildError::InvalidCvssScore),
Some(score) if !(0.0..=10.0).contains(&score) => Err(FindingBuildError::InvalidCvssScore),
Some(score) => Ok(Some(score)),
None => Ok(None),
}
}