mirror of
https://github.com/deps-rs/deps.rs.git
synced 2024-11-21 18:06:30 +00:00
Only query advisory database on latest matching version (#98)
* Add methods to check always insecure dependencies Unlike checks for `_insecure`, `always_insecure_ only accounts for vulnerabilities not patched in the latest version in the range * Update status renders to show "maybe insecure" - show always insecure dependencies as insecure, and remaining ones as "possibly insecure" - show warning sign on all dependencies with possible vulnerability - tweak security banner in case all insecure dependencies are "possibly insecure" * Update badge renderer to show "maybe insecure" - only show the red "inscure" if >=1 dependency is always insecure - show "possibly insecure" if all are up to date but might be vulnerable * Update status renderer - more complete counts per project * Format code * Extend banner to explain what "maybe insecure" means
This commit is contained in:
parent
50d81a7a79
commit
6cd7256ee8
4 changed files with 88 additions and 14 deletions
|
@ -108,6 +108,13 @@ impl AnalyzeDependenciesOutcome {
|
|||
.any(|&(_, ref deps)| deps.count_insecure() > 0)
|
||||
}
|
||||
|
||||
/// Checks if any always insecure main or build dependencies exist in the scanned crates
|
||||
pub fn any_always_insecure(&self) -> bool {
|
||||
self.crates
|
||||
.iter()
|
||||
.any(|&(_, ref deps)| deps.count_always_insecure() > 0)
|
||||
}
|
||||
|
||||
/// Checks if any dev-dependencies in the scanned crates are either outdated or insecure
|
||||
pub fn any_dev_issues(&self) -> bool {
|
||||
self.crates
|
||||
|
|
|
@ -103,10 +103,27 @@ impl AnalyzedDependency {
|
|||
}
|
||||
}
|
||||
|
||||
/// Check whether this dependency has at least one known vulnerability
|
||||
/// in any version in the required version range.
|
||||
///
|
||||
/// Note that the vulnerability may (or not) already be patched
|
||||
/// in the latest version(s) in the range.
|
||||
pub fn is_insecure(&self) -> bool {
|
||||
!self.vulnerabilities.is_empty()
|
||||
}
|
||||
|
||||
/// Check whether this dependency has at laest one known vulnerability
|
||||
/// even when the latest version in the required range is used.
|
||||
pub fn is_always_insecure(&self) -> bool {
|
||||
if let Some(latest) = &self.latest {
|
||||
self.vulnerabilities
|
||||
.iter()
|
||||
.any(|a| a.versions.is_vulnerable(latest))
|
||||
} else {
|
||||
self.is_insecure()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_outdated(&self) -> bool {
|
||||
self.latest > self.latest_that_matches
|
||||
}
|
||||
|
@ -199,6 +216,23 @@ impl AnalyzedDependencies {
|
|||
main_insecure + build_insecure
|
||||
}
|
||||
|
||||
/// Returns the number of main and build dependencies
|
||||
/// which are vulnerable to security issues,
|
||||
/// even they are updated to the latest version in the required range.
|
||||
pub fn count_always_insecure(&self) -> usize {
|
||||
let main_insecure = self
|
||||
.main
|
||||
.iter()
|
||||
.filter(|&(_, dep)| dep.is_always_insecure())
|
||||
.count();
|
||||
let build_insecure = self
|
||||
.build
|
||||
.iter()
|
||||
.filter(|&(_, dep)| dep.is_always_insecure())
|
||||
.count();
|
||||
main_insecure + build_insecure
|
||||
}
|
||||
|
||||
/// Checks if any outdated main or build dependencies exist
|
||||
pub fn any_outdated(&self) -> bool {
|
||||
let main_any_outdated = self.main.iter().any(|(_, dep)| dep.is_outdated());
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::engine::AnalyzeDependenciesOutcome;
|
|||
pub fn badge(analysis_outcome: Option<&AnalyzeDependenciesOutcome>) -> Badge {
|
||||
let opts = match analysis_outcome {
|
||||
Some(outcome) => {
|
||||
if outcome.any_insecure() {
|
||||
if outcome.any_always_insecure() {
|
||||
BadgeOptions {
|
||||
subject: "dependencies".into(),
|
||||
status: "insecure".into(),
|
||||
|
@ -23,10 +23,18 @@ pub fn badge(analysis_outcome: Option<&AnalyzeDependenciesOutcome>) -> Badge {
|
|||
color: "#dfb317".into(),
|
||||
}
|
||||
} else if total > 0 {
|
||||
BadgeOptions {
|
||||
subject: "dependencies".into(),
|
||||
status: "up to date".into(),
|
||||
color: "#4c1".into(),
|
||||
if outcome.any_insecure() {
|
||||
BadgeOptions {
|
||||
subject: "dependencies".into(),
|
||||
status: "maybe insecure".into(),
|
||||
color: "#8b1".into(),
|
||||
}
|
||||
} else {
|
||||
BadgeOptions {
|
||||
subject: "dependencies".into(),
|
||||
status: "up to date".into(),
|
||||
color: "#4c1".into(),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
BadgeOptions {
|
||||
|
|
|
@ -47,6 +47,10 @@ fn dependency_tables(crate_name: &CrateName, deps: &AnalyzedDependencies) -> Mar
|
|||
|
||||
fn dependency_table(title: &str, deps: &IndexMap<CrateName, AnalyzedDependency>) -> Markup {
|
||||
let count_total = deps.len();
|
||||
let count_always_insecure = deps
|
||||
.iter()
|
||||
.filter(|&(_, dep)| dep.is_always_insecure())
|
||||
.count();
|
||||
let count_insecure = deps.iter().filter(|&(_, dep)| dep.is_insecure()).count();
|
||||
let count_outdated = deps.iter().filter(|&(_, dep)| dep.is_outdated()).count();
|
||||
|
||||
|
@ -55,11 +59,15 @@ fn dependency_table(title: &str, deps: &IndexMap<CrateName, AnalyzedDependency>)
|
|||
html! {
|
||||
h3 class="title is-4" { (title) }
|
||||
p class="subtitle is-5" {
|
||||
(match (count_outdated, count_insecure) {
|
||||
(0, 0) => format!("({} total, all up-to-date)", count_total),
|
||||
(0, _) => format!("({} total, {} insecure)", count_total, count_insecure),
|
||||
(_, 0) => format!("({} total, {} outdated)", count_total, count_outdated),
|
||||
(_, _) => format!("({} total, {} outdated, {} insecure)", count_total, count_outdated, count_insecure),
|
||||
(match (count_outdated, count_always_insecure, count_insecure - count_always_insecure) {
|
||||
(0, 0, 0) => format!("({} total, all up-to-date)", count_total),
|
||||
(0, 0, c) => format!("({} total, {} possibly insecure)", count_total, c),
|
||||
(_, 0, 0) => format!("({} total, {} outdated)", count_total, count_outdated),
|
||||
(0, _, 0) => format!("({} total, {} insecure)", count_total, count_always_insecure),
|
||||
(0, _, c) => format!("({} total, {} insecure, {} possibly insecure)", count_total, count_always_insecure, c),
|
||||
(_, 0, c) => format!("({} total, {} outdated, {} possibly insecure)", count_total, count_outdated, c),
|
||||
(_, _, 0) => format!("({} total, {} outdated, {} insecure)", count_total, count_outdated, count_always_insecure),
|
||||
(_, _, c) => format!("({} total, {} outdated, {} insecure, {} possibly insecure)", count_total, count_outdated, count_always_insecure, c),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -81,6 +89,11 @@ fn dependency_table(title: &str, deps: &IndexMap<CrateName, AnalyzedDependency>)
|
|||
}
|
||||
{ "\u{00A0}" } // non-breaking space
|
||||
a href=(dep.deps_rs_path(name.as_ref())) { (name.as_ref()) }
|
||||
|
||||
@if dep.is_insecure() {
|
||||
{ "\u{00A0}" } // non-breaking space
|
||||
a href="#vulnerabilities" title="has known vulnerabilities" { "⚠️" }
|
||||
}
|
||||
}
|
||||
td class="has-text-right" { code { (dep.required.to_string()) } }
|
||||
td class="has-text-right" {
|
||||
|
@ -91,10 +104,12 @@ fn dependency_table(title: &str, deps: &IndexMap<CrateName, AnalyzedDependency>)
|
|||
}
|
||||
}
|
||||
td class="has-text-right" {
|
||||
@if dep.is_insecure() {
|
||||
@if dep.is_always_insecure() {
|
||||
span class="tag is-danger" { "insecure" }
|
||||
} @else if dep.is_outdated() {
|
||||
span class="tag is-warning" { "out of date" }
|
||||
} @else if dep.is_insecure() {
|
||||
span class="tag is-warning" { "maybe insecure" }
|
||||
} @else {
|
||||
span class="tag is-success" { "up to date" }
|
||||
}
|
||||
|
@ -288,9 +303,9 @@ fn render_success(
|
|||
|
||||
let status_data_uri = badge::badge(Some(&analysis_outcome)).to_svg_data_uri();
|
||||
|
||||
let hero_class = if analysis_outcome.any_insecure() {
|
||||
let hero_class = if analysis_outcome.any_always_insecure() {
|
||||
"is-danger"
|
||||
} else if analysis_outcome.any_outdated() {
|
||||
} else if analysis_outcome.any_insecure() || analysis_outcome.any_outdated() {
|
||||
"is-warning"
|
||||
} else {
|
||||
"is-success"
|
||||
|
@ -318,7 +333,7 @@ fn render_success(
|
|||
}
|
||||
section class="section" {
|
||||
div class="container" {
|
||||
@if analysis_outcome.any_insecure() {
|
||||
@if analysis_outcome.any_always_insecure() {
|
||||
div class="notification is-warning" {
|
||||
p { "This project contains "
|
||||
b { "known security vulnerabilities" }
|
||||
|
@ -326,6 +341,16 @@ fn render_success(
|
|||
a href="#vulnerabilities" { "bottom"} "."
|
||||
}
|
||||
}
|
||||
} @else if analysis_outcome.any_insecure() {
|
||||
div class="notification is-warning" {
|
||||
p { "This project might be open to "
|
||||
b { "known security vulnerabilities" }
|
||||
", which can be prevented by tightening "
|
||||
"the version range of affected dependencies. "
|
||||
"Find detailed information at the "
|
||||
a href="#vulnerabilities" { "bottom"} "."
|
||||
}
|
||||
}
|
||||
} @else if analysis_outcome.any_dev_issues() {
|
||||
(render_dev_dependency_box(&analysis_outcome))
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue