mirror of
https://github.com/deps-rs/deps.rs.git
synced 2024-11-22 10:26:30 +00:00
Display rustsec information on page (#96)
* chore: Bump rustsec version * feat: display RustSec CVEs at the bottom This closes #75. * fix: Reduce complexity and remove duplicate advisories
This commit is contained in:
parent
3e77c30ada
commit
7ebffe019f
5 changed files with 201 additions and 28 deletions
67
Cargo.lock
generated
67
Cargo.lock
generated
|
@ -142,7 +142,6 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"serde",
|
|
||||||
"time",
|
"time",
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
@ -417,6 +416,15 @@ dependencies = [
|
||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getopts"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.1.15"
|
version = "0.1.15"
|
||||||
|
@ -535,6 +543,22 @@ version = "0.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47"
|
checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humantime"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humantime-serde"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac34a56cfd4acddb469cc7fff187ed5ac36f498ba085caf8bbc725e3ff474058"
|
||||||
|
dependencies = [
|
||||||
|
"humantime",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "0.14.2"
|
version = "0.14.2"
|
||||||
|
@ -1047,6 +1071,18 @@ dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pulldown-cmark"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"getopts",
|
||||||
|
"memchr",
|
||||||
|
"unicase",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
|
@ -1187,23 +1223,25 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustsec"
|
name = "rustsec"
|
||||||
version = "0.22.2"
|
version = "0.23.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e5982d0d4f57176e3e8d62452a5d6dab98906ee5ab4ad1cb63c0877f0a16ab0e"
|
checksum = "7989ff58001a2be1c17945e53d32fcad5cf33c638a4b1239d6e1c9aff7a5d2f8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cargo-lock",
|
"cargo-lock",
|
||||||
"chrono",
|
|
||||||
"crates-index 0.16.2",
|
"crates-index 0.16.2",
|
||||||
"cvss",
|
"cvss",
|
||||||
"fs-err",
|
"fs-err",
|
||||||
"git2",
|
"git2",
|
||||||
"home",
|
"home",
|
||||||
|
"humantime",
|
||||||
|
"humantime-serde",
|
||||||
"platforms",
|
"platforms",
|
||||||
"semver 0.11.0",
|
"semver 0.11.0",
|
||||||
"serde",
|
"serde",
|
||||||
"smol_str",
|
"smol_str",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"toml",
|
"toml",
|
||||||
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1377,6 +1415,7 @@ dependencies = [
|
||||||
"maud",
|
"maud",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"pin-project 1.0.2",
|
"pin-project 1.0.2",
|
||||||
|
"pulldown-cmark",
|
||||||
"relative-path",
|
"relative-path",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"route-recognizer",
|
"route-recognizer",
|
||||||
|
@ -1456,9 +1495,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smol_str"
|
name = "smol_str"
|
||||||
version = "0.1.16"
|
version = "0.1.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2f7909a1d8bc166a862124d84fdc11bda0ea4ed3157ccca662296919c2972db1"
|
checksum = "6ca0f7ce3a29234210f0f4f0b56f8be2e722488b95cb522077943212da3b32eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
@ -1706,6 +1745,15 @@ version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicase"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
|
||||||
|
dependencies = [
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-bidi"
|
name = "unicode-bidi"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
|
@ -1724,6 +1772,12 @@ dependencies = [
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -1740,6 +1794,7 @@ dependencies = [
|
||||||
"idna",
|
"idna",
|
||||||
"matches",
|
"matches",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -21,11 +21,12 @@ hyper = { version = "0.14", features = ["full"] }
|
||||||
indexmap = { version = "1", features = ["serde-1"] }
|
indexmap = { version = "1", features = ["serde-1"] }
|
||||||
lru_time_cache = "0.11.1"
|
lru_time_cache = "0.11.1"
|
||||||
maud = "0.22.1"
|
maud = "0.22.1"
|
||||||
|
pulldown-cmark = "0.8"
|
||||||
once_cell = "1"
|
once_cell = "1"
|
||||||
pin-project = "1"
|
pin-project = "1"
|
||||||
relative-path = { version = "1.3", features = ["serde"] }
|
relative-path = { version = "1.3", features = ["serde"] }
|
||||||
route-recognizer = "0.3"
|
route-recognizer = "0.3"
|
||||||
rustsec = "0.22"
|
rustsec = "0.23"
|
||||||
crates-index = "0.15" # held back by semver 0.11
|
crates-index = "0.15" # held back by semver 0.11
|
||||||
semver = { version = "0.10", features = ["serde"] } # held at 0.10 due to #63
|
semver = { version = "0.10", features = ["serde"] } # held at 0.10 due to #63
|
||||||
reqwest = { version = "0.11", features = ["json"] }
|
reqwest = { version = "0.11", features = ["json"] }
|
||||||
|
|
|
@ -42,11 +42,12 @@ impl DependencyAnalyzer {
|
||||||
let version: cargo_lock::Version = ver.to_string().parse().unwrap();
|
let version: cargo_lock::Version = ver.to_string().parse().unwrap();
|
||||||
let query = database::Query::new().package_version(name, version);
|
let query = database::Query::new().package_version(name, version);
|
||||||
|
|
||||||
if !advisory_db
|
if let Some(db) = advisory_db {
|
||||||
.map(|db| db.query(&query).is_empty())
|
let vulnerabilities = db.query(&query);
|
||||||
.unwrap_or(true)
|
if !vulnerabilities.is_empty() {
|
||||||
{
|
dep.vulnerabilities =
|
||||||
dep.insecure = true;
|
vulnerabilities.into_iter().map(|v| v.to_owned()).collect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !ver.is_prerelease() {
|
if !ver.is_prerelease() {
|
||||||
|
|
|
@ -3,6 +3,7 @@ use std::{borrow::Borrow, str::FromStr};
|
||||||
use anyhow::{anyhow, Error};
|
use anyhow::{anyhow, Error};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use relative_path::RelativePathBuf;
|
use relative_path::RelativePathBuf;
|
||||||
|
use rustsec::Advisory;
|
||||||
use semver::{Version, VersionReq};
|
use semver::{Version, VersionReq};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
|
@ -89,7 +90,7 @@ pub struct AnalyzedDependency {
|
||||||
pub required: VersionReq,
|
pub required: VersionReq,
|
||||||
pub latest_that_matches: Option<Version>,
|
pub latest_that_matches: Option<Version>,
|
||||||
pub latest: Option<Version>,
|
pub latest: Option<Version>,
|
||||||
pub insecure: bool,
|
pub vulnerabilities: Vec<Advisory>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnalyzedDependency {
|
impl AnalyzedDependency {
|
||||||
|
@ -98,10 +99,14 @@ impl AnalyzedDependency {
|
||||||
required,
|
required,
|
||||||
latest_that_matches: None,
|
latest_that_matches: None,
|
||||||
latest: None,
|
latest: None,
|
||||||
insecure: false,
|
vulnerabilities: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_insecure(&self) -> bool {
|
||||||
|
!self.vulnerabilities.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_outdated(&self) -> bool {
|
pub fn is_outdated(&self) -> bool {
|
||||||
self.latest > self.latest_that_matches
|
self.latest > self.latest_that_matches
|
||||||
}
|
}
|
||||||
|
@ -181,8 +186,16 @@ impl AnalyzedDependencies {
|
||||||
|
|
||||||
/// Returns the number of insecure main and build dependencies
|
/// Returns the number of insecure main and build dependencies
|
||||||
pub fn count_insecure(&self) -> usize {
|
pub fn count_insecure(&self) -> usize {
|
||||||
let main_insecure = self.main.iter().filter(|&(_, dep)| dep.insecure).count();
|
let main_insecure = self
|
||||||
let build_insecure = self.build.iter().filter(|&(_, dep)| dep.insecure).count();
|
.main
|
||||||
|
.iter()
|
||||||
|
.filter(|&(_, dep)| dep.is_insecure())
|
||||||
|
.count();
|
||||||
|
let build_insecure = self
|
||||||
|
.build
|
||||||
|
.iter()
|
||||||
|
.filter(|&(_, dep)| dep.is_insecure())
|
||||||
|
.count();
|
||||||
main_insecure + build_insecure
|
main_insecure + build_insecure
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,14 +216,17 @@ impl AnalyzedDependencies {
|
||||||
|
|
||||||
/// Counts the number of insecure `dev-dependencies`
|
/// Counts the number of insecure `dev-dependencies`
|
||||||
pub fn count_dev_insecure(&self) -> usize {
|
pub fn count_dev_insecure(&self) -> usize {
|
||||||
self.dev.iter().filter(|&(_, dep)| dep.insecure).count()
|
self.dev
|
||||||
|
.iter()
|
||||||
|
.filter(|&(_, dep)| dep.is_insecure())
|
||||||
|
.count()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if any dev-dependencies are either insecure or outdated.
|
/// Returns `true` if any dev-dependencies are either insecure or outdated.
|
||||||
pub fn any_dev_issues(&self) -> bool {
|
pub fn any_dev_issues(&self) -> bool {
|
||||||
self.dev
|
self.dev
|
||||||
.iter()
|
.iter()
|
||||||
.any(|(_, dep)| dep.is_outdated() || dep.insecure)
|
.any(|(_, dep)| dep.is_outdated() || dep.is_insecure())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use hyper::{Body, Response};
|
use hyper::{Body, Response};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use maud::{html, Markup};
|
use maud::{html, Markup, PreEscaped};
|
||||||
|
use pulldown_cmark::{html, Parser};
|
||||||
|
use rustsec::advisory::Advisory;
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
|
|
||||||
use crate::engine::AnalyzeDependenciesOutcome;
|
use crate::engine::AnalyzeDependenciesOutcome;
|
||||||
|
@ -17,7 +19,7 @@ fn get_crates_version_url(name: impl AsRef<str>, version: &Version) -> String {
|
||||||
format!("https://crates.io/crates/{}/{}", name.as_ref(), version)
|
format!("https://crates.io/crates/{}/{}", name.as_ref(), version)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dependency_tables(crate_name: CrateName, deps: AnalyzedDependencies) -> Markup {
|
fn dependency_tables(crate_name: &CrateName, deps: &AnalyzedDependencies) -> Markup {
|
||||||
html! {
|
html! {
|
||||||
h2 class="title is-3" {
|
h2 class="title is-3" {
|
||||||
"Crate "
|
"Crate "
|
||||||
|
@ -29,22 +31,22 @@ fn dependency_tables(crate_name: CrateName, deps: AnalyzedDependencies) -> Marku
|
||||||
}
|
}
|
||||||
|
|
||||||
@if !deps.main.is_empty() {
|
@if !deps.main.is_empty() {
|
||||||
(dependency_table("Dependencies", deps.main))
|
(dependency_table("Dependencies", &deps.main))
|
||||||
}
|
}
|
||||||
|
|
||||||
@if !deps.dev.is_empty() {
|
@if !deps.dev.is_empty() {
|
||||||
(dependency_table("Dev dependencies", deps.dev))
|
(dependency_table("Dev dependencies", &deps.dev))
|
||||||
}
|
}
|
||||||
|
|
||||||
@if !deps.build.is_empty() {
|
@if !deps.build.is_empty() {
|
||||||
(dependency_table("Build dependencies", deps.build))
|
(dependency_table("Build dependencies", &deps.build))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dependency_table(title: &str, deps: IndexMap<CrateName, AnalyzedDependency>) -> Markup {
|
fn dependency_table(title: &str, deps: &IndexMap<CrateName, AnalyzedDependency>) -> Markup {
|
||||||
let count_total = deps.len();
|
let count_total = deps.len();
|
||||||
let count_insecure = deps.iter().filter(|&(_, dep)| dep.insecure).count();
|
let count_insecure = deps.iter().filter(|&(_, dep)| dep.is_insecure()).count();
|
||||||
let count_outdated = deps.iter().filter(|&(_, dep)| dep.is_outdated()).count();
|
let count_outdated = deps.iter().filter(|&(_, dep)| dep.is_outdated()).count();
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
|
@ -86,7 +88,7 @@ fn dependency_table(title: &str, deps: IndexMap<CrateName, AnalyzedDependency>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
td class="has-text-right" {
|
td class="has-text-right" {
|
||||||
@if dep.insecure {
|
@if dep.is_insecure() {
|
||||||
span class="tag is-danger" { "insecure" }
|
span class="tag is-danger" { "insecure" }
|
||||||
} @else if dep.is_outdated() {
|
} @else if dep.is_outdated() {
|
||||||
span class="tag is-warning" { "out of date" }
|
span class="tag is-warning" { "out of date" }
|
||||||
|
@ -147,6 +149,92 @@ fn render_dev_dependency_box(outcome: &AnalyzeDependenciesOutcome) -> Markup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_rustsec_link(advisory: &Advisory) -> String {
|
||||||
|
format!(
|
||||||
|
"https://rustsec.org/advisories/{}.html",
|
||||||
|
advisory.id().as_str()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_markdown(description: &str) -> Markup {
|
||||||
|
let mut rendered = String::new();
|
||||||
|
html::push_html(&mut rendered, Parser::new(description));
|
||||||
|
PreEscaped(rendered)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders a list of all security vulnerabilities affecting the repository
|
||||||
|
fn vulnerability_list(analysis_outcome: &AnalyzeDependenciesOutcome) -> Markup {
|
||||||
|
let mut vulnerabilities = Vec::new();
|
||||||
|
for (_, analyzed_crate) in &analysis_outcome.crates {
|
||||||
|
vulnerabilities.extend(
|
||||||
|
&mut analyzed_crate
|
||||||
|
.main
|
||||||
|
.iter()
|
||||||
|
.filter(|&(_, dep)| dep.is_insecure())
|
||||||
|
.map(|(_, dep)| &dep.vulnerabilities),
|
||||||
|
);
|
||||||
|
vulnerabilities.extend(
|
||||||
|
&mut analyzed_crate
|
||||||
|
.dev
|
||||||
|
.iter()
|
||||||
|
.filter(|&(_, dep)| dep.is_insecure())
|
||||||
|
.map(|(_, dep)| &dep.vulnerabilities),
|
||||||
|
);
|
||||||
|
vulnerabilities.extend(
|
||||||
|
&mut analyzed_crate
|
||||||
|
.build
|
||||||
|
.iter()
|
||||||
|
.filter(|&(_, dep)| dep.is_insecure())
|
||||||
|
.map(|(_, dep)| &dep.vulnerabilities),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// flatten Vec<Vec<&Advisory>> -> Vec<&Advisory>
|
||||||
|
let mut vulnerabilities: Vec<&Advisory> = vulnerabilities.into_iter().flatten().collect();
|
||||||
|
vulnerabilities.sort_unstable_by_key(|&v| v.id());
|
||||||
|
vulnerabilities.dedup();
|
||||||
|
|
||||||
|
html! {
|
||||||
|
h3 class="title is-3" id="vulnerabilities" { "Security Vulnerabilities" }
|
||||||
|
|
||||||
|
@for vuln in vulnerabilities {
|
||||||
|
div class="box" {
|
||||||
|
h3 class="title is-4" { code { (vuln.metadata.package.as_str()) } ": " (vuln.title()) }
|
||||||
|
p class="subtitle is-5" style="margin-top: -0.5rem;" { a href=(build_rustsec_link(vuln)) { (vuln.id()) } }
|
||||||
|
|
||||||
|
article { (render_markdown(vuln.description())) }
|
||||||
|
|
||||||
|
nav class="level" style="margin-top: 1rem;" {
|
||||||
|
div class="level-item has-text-centered" {
|
||||||
|
div {
|
||||||
|
p class="heading" { "Unaffected" }
|
||||||
|
@if vuln.versions.unaffected.is_empty() {
|
||||||
|
p class="is-grey" { "None"}
|
||||||
|
} @else {
|
||||||
|
@for item in &vuln.versions.unaffected {
|
||||||
|
p { code { (item) } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div class="level-item has-text-centered" {
|
||||||
|
div {
|
||||||
|
p class="heading" { "Patched" }
|
||||||
|
@if vuln.versions.unaffected.is_empty() {
|
||||||
|
p class="has-text-grey" { "None"}
|
||||||
|
} @else {
|
||||||
|
@for item in &vuln.versions.patched {
|
||||||
|
p { code { (item) } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render_failure(subject_path: SubjectPath) -> Markup {
|
fn render_failure(subject_path: SubjectPath) -> Markup {
|
||||||
html! {
|
html! {
|
||||||
section class="hero is-light" {
|
section class="hero is-light" {
|
||||||
|
@ -220,12 +308,24 @@ fn render_success(
|
||||||
}
|
}
|
||||||
section class="section" {
|
section class="section" {
|
||||||
div class="container" {
|
div class="container" {
|
||||||
@if analysis_outcome.any_dev_issues() {
|
@if analysis_outcome.any_insecure() {
|
||||||
|
div class="notification is-warning" {
|
||||||
|
p { "This project contains "
|
||||||
|
b { "known security vulnerabilities" }
|
||||||
|
". Find detailed information at the "
|
||||||
|
a href="#vulnerabilities" { "bottom"} "."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} @else if analysis_outcome.any_dev_issues() {
|
||||||
(render_dev_dependency_box(&analysis_outcome))
|
(render_dev_dependency_box(&analysis_outcome))
|
||||||
}
|
}
|
||||||
@for (crate_name, deps) in analysis_outcome.crates {
|
@for (crate_name, deps) in &analysis_outcome.crates {
|
||||||
(dependency_tables(crate_name, deps))
|
(dependency_tables(crate_name, deps))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if analysis_outcome.any_insecure() {
|
||||||
|
(vulnerability_list(&analysis_outcome))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(super::render_footer(Some(analysis_outcome.duration)))
|
(super::render_footer(Some(analysis_outcome.duration)))
|
||||||
|
|
Loading…
Reference in a new issue